Small improvements, cleanup

This commit is contained in:
Alex Swindler
2015-05-24 21:32:03 -06:00
parent 803a7d2a99
commit 21a6d5aa88
10 changed files with 156 additions and 160 deletions

View File

@@ -2,13 +2,13 @@
C++ Best Practices: A Forkable Coding Standards Document
This document is meant to be a collaborative discussion of the best practices in C++. It complements 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.
This document is meant to be a collaborative discussion of the best practices in C++. It complements 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.
In all cases brevity and succinctness is preferred. Examples are preferred for making the case for why one option is preferred over another. If necessary, words will be used.
<a rel="license" href="http://creativecommons.org/licenses/by-nc/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">C++ Best Practices</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://cppbestpractices.com" property="cc:attributionName" rel="cc:attributionURL">Jason Turner</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc/4.0/">Creative Commons Attribution-NonCommercial 4.0 International License</a>.
*Disclaimer*
This document is based on my personal experiences. You are not supposed to agree with it 100%. It exists as a book on [github](https://github.com/lefticus/cppbestpractices) so that you can fork it for your own uses or submit back proposed changes for everyone to share.
This document is based on my personal experiences. You are not supposed to agree with it 100%. It exists as a book on [GitHub](https://github.com/lefticus/cppbestpractices) so that you can fork it for your own uses or submit back proposed changes for everyone to share.

View File

@@ -6,23 +6,23 @@ An automated framework for executing these tools should be established very earl
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
* [GitLab](https://gitlab.com/), Subversion, Bitkeeper, many many others... The above are the most popular free services.
* [GitHub](https://github.com/) - allows for unlimited public repositories, must pay for a private repository.
* [Bitbucket](https://bitbucket.org/) - allows for unlimited private repositories with up to 5 collaborators, for free.
* [SourceForge](http://sourceforge.net/) - open source hosting only.
* [GitLab](https://gitlab.com/), Subversion, BitKeeper, many many others... The above are the most popular free services.
## 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)
* [biicode](http://biicode.com)
* [waf](https://waf.io/)
* [CMake](http://www.cmake.org/)
* [Biicode](https://www.biicode.com/)
* [Waf](https://waf.io/)
* [FASTBuild](http://www.fastbuild.org/)
* [ninja](https://martine.github.io/ninja/) - can greatly improve the incremental build time of your larger projects. Can be used as a target for cmake
* google's build tool
* [Ninja](https://martine.github.io/ninja/) - can greatly improve the incremental build time of your larger projects. Can be used as a target for CMake.
* [Bazel](http://bazel.io/)
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
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
@@ -32,16 +32,17 @@ Continuous Integration (CI) tools automatically build the source code as changes
* [Travis CI](http://travis-ci.org)
* works well with C++
* designed for use with github
* free for public repositories on github
* Hudson CI
* Appveyor
* designed for use with GitHub
* free for public repositories on GitHub
* [Hudson CI](http://hudson-ci.org/)
* [AppVeyor](http://www.appveyor.com/)
* supports Windows and MSVC
* [Decent CI](https://github.com/lefticus/decent_ci)
* simple ad-hoc continuous integration that posts results to github
* supports Windows, MacOS and Linux
* simple ad-hoc continuous integration that posts results to GitHub
* supports Windows, OS X, and Linux
* used by [ChaiScript](http://chaiscript.com/ChaiScript-BuildResults/full_dashboard.html)
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
If you have an open source, 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
@@ -58,7 +59,7 @@ You should use as many compilers as you can for your platform(s). Each compiler
* `-Wshadow` warn the user if a variable declaration 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
* `-Wold-style-cast` warn for c-style casts
* `-Wcast-align` warn for potential performance problem casts
* `-Wcast-align` warn for potential performance problem casts
* `-Wunused` warn on anything being unused
* `-Woverloaded-virtual` warn if you overload (not override) a virtual function
* `-pedantic`
@@ -74,37 +75,33 @@ You should use as many compilers as you can for your platform(s). Each compiler
Not recommended
* `/Wall` - Also warns on files included from the standard library, so it's not very useful and creates too many extra warnings.
* `/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.
Start with very strict warning 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
Consider using the *treat warnings as errors* setting. `/Wx` with MSVC, `-Werror` with GCC / Clang
## llvm based tools
## 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
* [include-what-you-use](https://code.google.com/p/include-what-you-use/), [example results](https://github.com/ChaiScript/ChaiScript/commit/c0bf6ee99dac14a19530179874f6c95255fde173)
* [clang-modernize](http://clang.llvm.org/extra/clang-modernize.html), [example results](https://github.com/ChaiScript/ChaiScript/commit/6eab8ddfe154a4ebbe956a5165b390ee700fae1b)
* [clang-check](http://clang.llvm.org/docs/ClangCheck.html)
* [clang-tidy](http://clang.llvm.org/extra/clang-tidy.html)
## Static Analyzers
The best bet is the static analyzer that you can run as part of your automated build system. cppcheck and clang meet that requirement for free options.
The best bet is the static analyzer that you can run as part of your automated build system. Cppcheck and clang meet that requirement for free options.
### cppcheck
Cppcheck is free and opensource. It strives for 0 false positives and does a good job at it. Therefore all warnings should be enabled: `-enable=all`
### Cppcheck
Cppcheck is free and open source. It strives for 0 false positives and does a good job at it. Therefore all warnings 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.
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](#llvm-based-tools).
### MSVC's Static Analyzer
@@ -112,11 +109,11 @@ Can be enabled with the `/analyze` [command line option](http://msdn.microsoft.c
### ReSharper C++ / CLion
Both of these tools from [JetBrains](https://www.jetbrains.com/cpp/) offer some level of static analysis and automated fixes for common things that can be done better. They have options available for free licenses for Open Source project leaders.
Both of these tools from [JetBrains](https://www.jetbrains.com/cpp/) offer some level of static analysis and automated fixes for common things that can be done better. They have options available for free licenses for open source project leaders.
### Qt Creator
Qt Creator can plug into the clang static analyzer, but *only* on the *commercial* version of Qt Creator.
Qt Creator can plug into the clang static analyzer, but *only* on the *commercial* version of Qt Creator.
### Metrix++
@@ -130,22 +127,22 @@ While not necessarily a static analyzer, Metrix++ can identify and report on the
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.
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.
An alternative to lcov is [gcovr](http://gcovr.com/). It seems to provide similar functionality, but is written in python.
<link to chaiscript example of using it>
An alternative to LCOV is [Gcovr](http://gcovr.com/). It seems to provide similar functionality, but is written in Python.
<link to ChaiScript example of using it>
### Valgrind
Runtime code analyzer that can detect memory leaks, race conditions and other associated problems. It is supported on various unix platforms.
Runtime code analyzer that can detect memory leaks, race conditions, and other associated problems. It is supported on various Unix platforms.
### GCC/Clang Sanitizers
### GCC / Clang Sanitizers
These tools provide many of the same features as valgrind, but built into the compiler. They are easy to use and provide a report of what went wrong.
These tools provide many of the same features as Valgrind, but built into the compiler. They are easy to use and provide a report of what went wrong.
* address
* thread
* undefined
* AddressSanitizer
* ThreadSanitizer
* UndefinedBehaviorSanitizer
## Ignoring Warnings
@@ -156,7 +153,7 @@ If it is determined by team consensus that the compiler or analyzer is warning o
CMake, mentioned above, has a built in framework for executing tests. Make sure whatever build system you use has a way to execute tests built in.
To further aid in executing tests, consider a library such as [googletest](https://code.google.com/p/googletest/) or [Catch](https://github.com/philsquared/Catch) to help you organize the tests.
To further aid in executing tests, consider a library such as [Google Test](https://code.google.com/p/googletest/) or [Catch](https://github.com/philsquared/Catch) to help you organize the tests.
### Unit Tests
@@ -164,7 +161,7 @@ Unit tests are for small chunks of code, individual functions which can be teste
### Integration Tests
There should be a test enabled for every feature or bug fix that is committed. See also "Code Coverage Analysis." These are tests that are higher level than unit tests. They should still be limited in scope to individual features.
There should be a test enabled for every feature or bug fix that is committed. See also [Code Coverage Analysis](#code-coverage-analysis). These are tests that are higher level than unit tests. They should still be limited in scope to individual features.
### Negative Testing

View File

@@ -1,12 +1,13 @@
# 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
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.
are common examples. *snake_case* has the advantage that it can also work with spell checkers, if desired.
## Common C++ Naming Conventions
@@ -14,11 +15,11 @@ are common examples. snake_case has the advantage that it can also work with spe
* functions and variables start with lower case: `myMethod`
* constants are all capital: `const int PI=3.14159265358979323;`
C++ Standard Library (and other well known C++ libraries like boost) use these guidelines:
C++ Standard Library (and other well-known C++ libraries like [Boost](http://www.boost.org/)) use these guidelines:
* Macro names use uppercase with underscores: INT_MAX
* Template parameter names use camel case: InputIterator
* All other names use snake case: unordered_map
* Macro names use uppercase with underscores: `INT_MAX`
* Template parameter names use camel case: `InputIterator`
* All other names use snake case: `unordered_map`
## Distinguish Private Object Data
@@ -26,18 +27,18 @@ Name private data with a `m_` prefix to distinguish it from public data. `m_` st
## Distinguish Function Parameters
Name function parameters with an `t_` prefix. `t_` can be thought of as "the," but the meaning is arbitrary. The point is to distinguish function parameters from other variables in scope while giving us a consistent naming strategy.
Name function parameters with an `t_` prefix. `t_` can be thought of as "the", but the meaning is arbitrary. The point is to distinguish function parameters from other variables in scope while giving us a consistent naming strategy.
By using `t_` for parameters and `m_` for module data, we can have consistency with both public members of structs and private members of classes.
By using `t_` for parameters and `m_` for module data, we can have consistency with both public members of structs and private members of classes.
Any prefix or postfix can be chosen for your organization. This is just one example.
```
```cpp
struct Size
{
int width;
int height;
ValueType(int t_width, int t_height) : width(t_width), height(t_height) {}
};
@@ -49,7 +50,7 @@ class PrivateSize
int width() const { return m_width; }
int height() const { return m_height; }
ValueType(int t_width, int t_height) : m_width(t_width), m_height(t_height) {}
private:
int m_width;
int m_height;
@@ -59,14 +60,14 @@ class PrivateSize
## Don't name anything starting with an `_`
## Don't Name Anything Starting With `_`
If you do, you risk broaching on names reserved for implementation use
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
## Well-Formed Example
```cpp
class MyClass
@@ -76,12 +77,12 @@ public:
: m_data(t_data)
{
}
int getData() const
{
return m_data;
}
private:
int m_data;
};
@@ -89,11 +90,9 @@ private:
## Enable out of source directory builds
## Enable Out-of-Source-Directory Builds
### Make sure generated files go into build folder
Not the source folder
Make sure generated files go into build folder, not the source folder
## Use `nullptr`
@@ -124,16 +123,16 @@ int myFunc()
which would be impossible if the function comment header used `/* */`
## Never Use `using namespace` In a Header File
## Never Use `using namespace` in a Header File
This causes the name space you are `using` to be pulled into the namespace of the header file.
It litters the own namespace and it may lead to name collisions in the future.
It litters the namespace and it may lead to name collisions in the future.
Writing `using namespace` in an implementation file is fine though.
## 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
Header files must contain a distinctly-named include guard to avoid problems with including the same header multiple times and to prevent conflicts with headers from other projects.
```cpp
#ifndef MYPROJECT_MYCLASS_HPP
@@ -147,11 +146,11 @@ class MyClass {
#endif
```
You may also consider to use the `#pragma once` directive instead which is quasi standard across many compilers.
It's short and makes the intent clear.
You may also consider using the `#pragma once` directive instead which is quasi-standard across many compilers.
It's short and makes the intent clear.
## {} are required for blocks.
## {} Are Required for Blocks.
Leaving them off can lead to semantic errors in the code.
```cpp
@@ -167,8 +166,8 @@ 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;
@@ -178,28 +177,28 @@ for (int i = 0; i < 15; ++i) {
}
```
## Keep lines a reasonable length
## Keep Lines a Reasonable Length
```cpp
// Bad Idea
// hard to follow
if (x && y && myFunctionThatReturnsBool() && caseNumber3 && (15 > 12 || 2 < 3)) {
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)) {
if (x && y && myFunctionThatReturnsBool()
&& caseNumber3
&& (15 > 12 || 2 < 3)) {
}
```
Many projects and coding standards have a soft guideline that one should try to use less than about 80 or 100 characters per line.
Such code is generally easier to read.
It also makes it possible to have two separate file next to each other on one screen without having a tiny font.
Many projects and coding standards have a soft guideline that one should try to use less than about 80 or 100 characters per line.
Such code is generally easier to read.
It also makes it possible to have two separate files next to each other on one screen without having a tiny font.
## Use "" For Including Local Files
## Use "" for Including Local Files
... `<>` is [reserved for system includes](http://blog2.emptycrate.com/content/when-use-include-verses-include).
```cpp
@@ -209,7 +208,7 @@ It also makes it possible to have two separate file next to each other on one sc
#include <includes/MyHeader.hpp>
// Worse Idea
// requires potentially even more specific -I directives and
// requires potentially even more specific -I directives and
// makes code more difficult to package and distribute
#include <string>
#include <MyHeader.hpp>
@@ -242,7 +241,7 @@ private:
// Good Idea
// C++'s member initializer list is unique to the language and leads to
// cleaner code and potential performance gains that other languages cannot
// cleaner code and potential performance gains that other languages cannot
// match
class MyClass
{
@@ -257,24 +256,24 @@ private:
};
```
In C++11 you may consider to always give each member a default value, e.g. by writing
In C++11 you may consider always giving each member a default value, e.g. by writing
```cpp
// ... //
private:
int m_value = 0;
// ... //
```
inside the class body. This makes sure that no constructor ever "forgets" to initialize a member object.
Forgetting to initialize a member is a source of undefined behaviour bugs which are often extremely hard to find.
inside the class body. This makes sure that no constructor ever "forgets" to initialize a member object.
Forgetting to initialize a member is a source of undefined behavior bugs which are often extremely hard to find.
## 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.
There is almost never a reason to declare an identifier in the global namespaces. Instead, functions and classes should exist in an appropriately named namespace or in a class inside of a namespace. Identifiers which are placed in the global namespace risk conflicting with identifiers from other libraries (mostly C, which doesn't have namespaces).
## 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.
Compiler definitions and macros are replaced by the preprocessor before the compiler is ever run. This can make debugging very difficult because the debugger doesn't know where the source came from.
```cpp
// Bad Idea
@@ -286,24 +285,24 @@ namespace my_project {
public:
// if the above macro would be expanded, then the following line would be:
// static const double 3.14159 = 3.14159;
// which leads to an compile-time error. Sometimes such errors are hard to understand.
// which leads to a compile-time error. Sometimes such errors are hard to understand.
static const double PI = 3.14159;
}
}
```
## Use the correct integer type for stdlib features
## Use the Correct Integer Type For stdlib Features
The standard library generally returns `size_t` for anything related to size. What exactly `size_t` is is implementation defined.
Make sure you stick with the correct integer types and be consistent with the C++ stdlib. It might not warn on the platform you are currently using, but it probably will when you change platforms.
Make sure you stick with the correct integer types and remain consistent with the C++ stdlib. It might not warn on the platform you are currently using, but it probably will when you change platforms.
## Use .hpp and .cpp for your file extensions
## 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.
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, Visual Studio only automatically recognizes .cpp and .cxx for C++ files, and 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 recognized and having the distinction is helpful.
One particularly large project ([OpenStudio](https://github.com/NREL/OpenStudio)) uses .hpp and .cpp for user-generated files and .hxx and .cxx for tool-generated files. Both are well recognized and having the distinction is helpful.
## Never Mix Tabs and Spaces
@@ -316,11 +315,11 @@ assert(registerSomeThing()); // make sure that registerSomeThing() returns true
```
The above code succeeds when making a debug build, but gets removed by the compiler when making a release build, giving you different behavior between debug and release builds.
This is because `assert()` is a macro which expands to nothing in release mode.
This is because `assert()` is a macro which expands to nothing in release mode.
## Don't be afraid of templates
## Don't Be Afraid of Templates
They can help you stick to DRY principles.
They should be preferred to macros, because macros do not honor namespaces, etc.
They can help you stick to [DRY principles](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself).
They should be preferred to macros, because macros do not honor namespaces, etc.
## Use operator overloads judiciously
## Use Operator Overloads Judiciously

View File

@@ -1,8 +1,8 @@
# 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.
## 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 has a side effect. 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
@@ -14,7 +14,7 @@ public:
{
}
std::string get_value()
std::string get_value()
{
return m_value;
}
@@ -43,7 +43,7 @@ private:
}
```
## Avoid raw memory access
## 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.
@@ -57,7 +57,7 @@ delete myobj;
// Good Idea
std::shared_ptr<MyClass> myobj = make_shared<MyClass>();
// ...
// ...
// myobj is automatically freed for you whenever it is no longer used.
```

View File

@@ -1,7 +1,7 @@
# Considering Portability
## Know your types
## 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.
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 container sizes are reported in `size_t`. If you get the handling of `size_t` wrong, you can create subtle lurking 64-bit issues that arise only after you start to overflow the indexing of 32-bit integers. char vs unsigned char.
http://www.viva64.com/en/a/0010/

View File

@@ -2,7 +2,7 @@
## Avoid Global Data
This includes statics and singletons
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.

View File

@@ -4,7 +4,7 @@
### Forward Declare when Possible
### Forward Declare When Possible
This:
@@ -31,7 +31,7 @@ This applies to templates as well:
template<typename T> class MyTemplatedType;
```
This is a proactive approach to simplify compilation time and rebuilding dependencies.
This is a proactive approach to reduce compilation time and rebuilding dependencies.
### Avoid Unnecessary Template Instantiations
@@ -39,7 +39,7 @@ Templates are not free to instantiate. Instantiating many templates, or template
For more examples see [this article](http://blog2.emptycrate.com/content/template-code-bloat-revisited-smaller-makeshared).
### Firewall Frequently Changing Header Files
## Firewall Frequently Changing Header Files
@@ -51,23 +51,24 @@ The compiler has to do something with each include directive it sees. Even if it
### 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
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
These are not meant to supersede good design
CCACHE, facebook's thing (warp)
* [ccache](https://ccache.samba.org/)
* [warp](https://github.com/facebook/warp), Facebook's preprocessor
### Put tmp on Ramdisk
See [this](https://www.youtube.com/watch?v=t4M3yG1dWho) youtube video for more details.
See [this](https://www.youtube.com/watch?v=t4M3yG1dWho) YouTube video for more details.
### Use the Gold Linker
### Use the gold linker
If on linux, consider using the gold linker for gcc.
If on Linux, consider using the gold linker for GCC.
## Runtime
@@ -80,12 +81,11 @@ There's no real way to know where your bottlenecks are without analyzing the cod
### Simplify the Code
The cleaner, more simple, and easier to read the code is, the better chance the compiler has at implementing it as well.
The cleaner, simpler, and easier to read the code is, the better chance the compiler has at implementing it well.
### Use Initializer Lists
```c++
```cpp
// This
std::vector<ModelObject> mos{mo1, mo2};
@@ -93,18 +93,18 @@ std::vector<ModelObject> mos{mo1, mo2};
auto mos = std::vector<ModelObject>{mo1, mo2};
```
```c++
```cpp
// Don't do this
std::vector<ModelObject> mos;
mos.push_back(mo1);
mos.push_back(mo2);
```
Initializer lists are significantly more efficient; reducing object copies and resizing of containers
Initializer lists are significantly more efficient; reducing object copies and resizing of containers.
### Reduce Temporary Objects
```c++
```cpp
// Instead of
auto mo1 = getSomeModelObject();
auto mo2 = getAnotherModelObject();
@@ -112,13 +112,13 @@ auto mo2 = getAnotherModelObject();
doSomething(mo1, mo2);
```
```c++
```cpp
// consider:
doSomething(getSomeModelObject(), getAnotherModelObject());
```
This sort of code prevents the compiler from performing a move operation
This sort of code prevents the compiler from performing a move operation...
### Enable move operations
@@ -128,21 +128,21 @@ Certain coding choices we make (such as declaring our own destructor or assignme
For most code, a simple
```c++
```cpp
ModelObject(ModelObject &&) = default;
```
would suffice. However, MSVC2013 doesnt seem to like this code yet.
would suffice. However, MSVC2013 doesn't seem to like this code yet.
### Kill shared_ptr Copies
### Kill `shared_ptr` Copies
shared_ptr objects are much more expensive to copy than you think they should be. This is because the reference count must be atomic and thread safe. So this comment just re-enforces the note above - avoid temporaries and too many copies of objects. Just because we are using a pImpl it does not mean our copies are free.
`shared_ptr` objects are much more expensive to copy than you'd think they would be. This is because the reference count must be atomic and thread-safe. So this comment just re-enforces the note above: avoid temporaries and too many copies of objects. Just because we are using a pImpl it does not mean our copies are free.
### Reduce Copies and Reassignments as Much as Possible
For more simple cases, the ternary operator can be used
For more simple cases, the ternary operator can be used:
```c++
```cpp
// Bad Idea
std::string somevalue;
@@ -153,14 +153,14 @@ if (caseA) {
}
```
```c++
```cpp
// Better Idea
const std::string somevalue = caseA?"Value A":"Value B";
```
More complex cases can be facilited with an [immediately-invoked lambda](http://blog2.emptycrate.com/content/complex-object-initialization-optimization-iife-c11).
More complex cases can be facilitated with an [immediately-invoked lambda](http://blog2.emptycrate.com/content/complex-object-initialization-optimization-iife-c11).
```c++
```cpp
// Bad Idea
std::string somevalue;
@@ -173,7 +173,7 @@ if (caseA) {
}
```
```c++
```cpp
// Better Idea
const std::string somevalue = [&](){
if (caseA) {
@@ -194,11 +194,11 @@ Exceptions which are thrown and captured internally during normal processing slo
### Get rid of “new”
We already know that we should not be using raw memory access, so we are using `unique_ptr` and `shared_ptr` instead, right?
Heap allocations are much more expensive than stack allocations, but sometimes we have to use them. To make matters worse, creating a shared_ptr actually requires 2 heap allocations.
Heap allocations are much more expensive than stack allocations, but sometimes we have to use them. To make matters worse, creating a `shared_ptr` actually requires 2 heap allocations.
However, the make_shared function reduces this down to just one.
However, the `make_shared` function reduces this down to just one.
```c++
```cpp
std::shared_ptr<ModelObject_Impl>(new ModelObject_Impl());
// should become
@@ -207,15 +207,15 @@ std::make_shared<ModelObject_Impl>(); // (it's also more readable and concise)
### Get rid of std::endl
`std::endl` implies a flush operation. Its equivalent to `"\n" << std::flush`.
`std::endl` implies a flush operation. It's equivalent to `"\n" << std::flush`.
### 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.
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
```cpp
// Good Idea
for (int i = 0; i < 15; ++i)
{
MyObject obj(i);
@@ -232,13 +232,13 @@ for (int i = 0; i < 15; ++i)
// obj is still taking up memory for no reason
```
### Prefer double to float
### Prefer `double` to `float`
Operations are doubles are typically faster than float. However, in vectorized operations, float might win out. Analyze the code and find out which is faster for your application!
Operations on `double`s are typically faster than `float`s. However, in vectorized operations, `float` might win out. Analyze the code and find out which is faster for your application!
### 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.
### Prefer `++i` to `i++`
... when it is semantically correct. Pre-increment is [faster](http://blog2.emptycrate.com/content/why-i-faster-i-c) than post-increment because it does not require a copy of the object to be made.
```cpp
// Bad Idea

View File

@@ -2,9 +2,9 @@
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
The VM based compiled languages have learned this already: JRuby, Jython, IronRuby, IronPython
* chaiscript
* angelscript
* luabind
* SWIG
* ChaiScript
* AngelScript
* luabind
* SWIG

View File

@@ -1,7 +1,7 @@
# 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://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

View File

@@ -1,4 +1,4 @@
# Final Thoughts
Expand your horizons and use other programming languages. Other languages have different constructs and expressions. Learning what else is out there will encourage you to be more creative with your C++ and write cleaner, more expresive code.
Expand your horizons and use other programming languages. Other languages have different constructs and expressions. Learning what else is out there will encourage you to be more creative with your C++ and write cleaner, more expressive code.