Introduction
This document details the coding practices that are used in the OpenVDB codebase. Contributed code should conform to these guidelines to maintain consistency and maintainability. If there is a rule that you would like clarified, changed, or added, please send a note to openv.nosp@m.db@g.nosp@m.mail..nosp@m.com.
Contents
Naming Conventions
Namespaces
- Lowercase words, keep simple and short (e.g.,
tools
, tree
).
Classes and Structs
- Mixed case; first letter uppercase (e.g.,
AffineMap
, TreeIterator
)
- Do not use a prefix.
Class Methods
- Mixed case; first letter lowercase (e.g.,
getAccessor()
, gridType()
)
- Accessors that return a member variable by reference or a primitive type by value are just the variable name (e.g.,
Grid::tree()
).
- Accessors that involve construction of objects or other computations are
get
+ the variable name (e.g., Grid::getAccessor()
).
- Simple mutators are
set
+ the variable name (e.g., Grid::setTree(tree);
).
Class Instance Variables
- Mixed case; always prefixed by
m
(e.g., mTree
, mTransform
)
Class Static Variables
- Mixed case; always prefixed by
s
(e.g., sInitialized
)
Local Variables and Arguments
- Use mixed case with an initial lower case (e.g.,
ijk
, offset
, range
, rhsValue
).
Constants
- All uppercase; words separated by underscores. If not in a namespace or local to a source file, then prefixed with the library name in all caps (e.g.,
HALF_FLOAT_TYPENAME_SUFFIX
, ZIP_COMPRESSION_LEVEL
).
Enumeration Names
- Mixed case; first letter uppercase (e.g.,
GridClass
, VecType
)
Enumeration Values
- Same as constants; all uppercase; words separated by underscores (e.g.,
GRID_LEVEL_SET
, VEC_INVARIANT
) and with a common prefix (GRID_
, VEC_
, etc.)
Typedefs
- Mixed case; first letter uppercase (e.g.,
ConstPtr
, ValueType
)
- Do not use a prefix.
Global Variables
- Mixed case; always prefixed by
g
(e.g., gRegistry
)
- In general, try to use class static data instead of globals.
Global Functions
- Mixed case; always prefixed by
g
(e.g., gFunc()
).
- In general, try to use class static members instead of global functions.
Booleans
- When naming boolean functions and variables, use names that read as a condition (e.g.,
if (grid.empty())
, if (matrix.isInvertible())
).
Practices
General
- Code must compile without any warning messages. if closely related.
- Prefer the C++ Standard Library to the C Standard Library.
- Restrict variables to the smallest scopes possible, and avoid defining local variables before giving them values. Prefer declarations inside conditionals:
if (Grid::Ptr grid = createGrid()) { ... }
instead of Grid::Ptr grid = createGrid(); if (grid) { ... }
- For new files, be sure to use the right license boilerplate per our license policy.
Formatting
- Use Doxygen-style comments to document public code.
- Indentation is 4 spaces. Do not use tabs.
- Use Unix-style carriage returns ("\n") rather than Windows/DOS ones ("\r\n").
- Don’t put an
else
right after a return
. It’s unnecessary and increases indentation level.
- Do not leave debug printfs or other debugging code lying around.
- Leave a blank line between a group of variable declarations and other code.
- Leave a space after the keywords
if
, switch
, while
, do
, for
, and return
.
- Leave a space on each side of binary operators such as +, -, *, /, &&, and ||. For clarity in mathematical situations, you may omit the spaces on either side of * and / operators to illustrate their precedence over + and -.
- Do not leave a space between any dereferencing operator (such as *, &, [], ->, or .) and its operands.
- In parameter lists, leave a space after each comma.
- Do not leave a space after an opening parenthesis or before a closing parenthesis.
- Parentheses should be used to clarify operator precedence in expressions.
- Do not leave a space before an end-of-statement semicolon.
- Do not use literal tabs in strings or character constants. Rather, use spaces or "\t".
- If a parameter list is too long, break it between parameters. Group related parameters on lines if appropriate.
- Modified spacing is allowed to vertically align repetitive expressions.
- Always begin numeric constants with a digit (e.g., 0.001 not .001).
- Use K&R-style brace placement in public code.
- You may leave off braces for simple, single line flow control statements.
- The return type in a function definition should go on a line by itself.
Include Statements
- Always use double quotes ("") to include header files that live in the same directory as your source code.
- Use angle brackets (<>) to include header files from outside a file’s directory.
- Do not use absolute paths in include directives.
- If there is a header corresponding to a given source file, list it first, followed by other local headers, library headers and then system headers.
Header Files
- Header files have a
.h
extension.
- All header files should be bracketed by
#ifdef
"guard" statements.
- In class definitions, list public, then protected, then private members.
- List methods before variables.
- Fully prototype all public functions and use descriptive naming for each argument.
- Declare every function defined in a header but outside a class definition explicitly as
inline
.
- Prefer forward declarations to
#include
directives in headers.
- Do not take advantage of indirect inclusion of header files.
Source Files
- Source files have a
.cc
extension.
- Properly prototype all file static functions with usefully named arguments.
- Whenever possible, put variables and functions in an anonymous namespace.
- Avoid global variables.
- For the main file of an executable, define
main()
at the top and then utility functions below it in a top-down fashion.
Comments
- Use
//
style comments instead of / * * / style, even for multi-line comments.
- Use multi-line comments to describe following paragraphs of code.
- Use end-of-line comments to describe variable declarations or to clarify a single statement.
- Large blocks of code should be commented out with
#if 0
and #endif
.
- Do not leave obsolete code fragments within comments as an historical trail.
Primitive Types
- Avoid writing code that is dependent on the bit size of primitive values, but when specific bit sizes are required, use explicitly-sized types such as
int32_t
or uint64_t
.
Macros
- Avoid macros for constants. Use static constants instead.
- Avoid macro functions. Use
inline
and templates instead.
Classes
- Constructors that can be called with only one argument should be prefixed with the
explicit
keyword to avoid unintended type conversions.
- Always list the destructor.
- Never call virtual methods from destructors.
- If you have a copy constructor, make sure you have an assignment operator.
- If you have an assignment operator, you probably need a copy constructor.
- If you have data members that are pointers to dynamically allocated memory, make sure you have a copy constructor and an assignment operator, both of which do the right thing with that memory.
- Classes which are to be subclassed always have a virtual destructor, even if it is empty.
- Check against self assignment and return
*this
in assignment operators.
- Declare methods as const as much as possible.
- Declare object-valued input arguments as const references wherever possible. Primitives may be passed by value, however.
- Arithmetic, logical, bitwise, dereference, and address of operators should only be used when their semantics are clear, obvious, and unambiguous.
- The application operator [ () ] is allowed for functors.
- Conversion operators should be avoided.
- Never return const references to stack allocated or implicitly computed objects.
- If a class does not have a copy constructor and/or assignment operator, consider creating a private unimplemented copy constructor and/or assignment operator to prevent automatically generated versions from being used.
Conditional Statements
- For test expressions, use a form that indicates as clearly as possible the types of operands by avoiding implicit casts.
- Assignments that occur within conditional statements must have no effect in the enclosing scope.
- Allow for arithmetic imprecision when comparing floating point values.
- In switch statements, always comment fallthroughs and empty cases.
Namespaces
- Namespaces should be used whenever possible.
- Avoid pulling in entire namespaces with
using
directives (e.g., using namespace std;
).
using
declarations are allowed for individual symbols (e.g., using std::vector;
), but they should never appear in a header file.
- Define global operators in the namespace of their arguments.
- Namespaces are not indented.
Exceptions
- Appropriate use of exceptions is encouraged.
- Methods should declare all exceptions they might throw using comments, but not exception specifications.
- Throw scope local exception instances, not pointers or references or globals.
- Catch exceptions by reference.
- Never allow an exception to escape from a destructor.
Templates
- Use
typename
rather than class
when declaring template type parameters.
Miscellaneous
- Don’t use pointers to run through arrays of non-primitive types. Use explicit array indexing, iterators or generic algorithms instead.
- Use C++ casts (
static_cast<int>(x)
or int(x)
), not C casts ((int)x
).
- Multiple variables of the same data type may be declared on the same line
- Library code must never deliberately terminate the application in response to an error condition.
- Avoid using malloc/free when new/delete can be used instead.
- Avoid
goto
.
- Avoid "magic numbers". Use named constants when necessary.
- If you use typeid/typeinfo, be aware that although all runtimes support
typeinfo::name()
, the format of the string it returns varies between compilers and even for a given compiler the value is not guaranteed to be unique.