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
).
- Use
internal
(or detail
) or module_internal
for symbols such as templates that must be defined in header files but that are implementation details not intended for public use.
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()
)
- Accessor methods that return a member variable by reference or a primitive type by value are just the variable name (e.g.,
Grid::tree()
).
- Accessor methods 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
)
- The
m
prefix may be omitted for members of struct
s used in the public API (e.g., struct Color { float r, g, b; }
).
Class Static Variables
- Mixed case; always prefixed by
s
(e.g., sInitialized
)
Local Variables and Arguments
- Use mixed case with an initial lower case letter (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
- With
enum
, all uppercase with words separated by underscores (e.g., GRID_LEVEL_SET
, VEC_INVARIANT
) and with a common prefix
- With
enum class
, mixed case with the first letter uppercase (e.g., GridClass::Unknown
, GridClass::LevelSet
)
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 at the default warning level.
- 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
- Indentation is 4 spaces. Do not use tabs.
- Lines are no more than 100 characters long.
- Use Unix-style carriage returns ("\n") rather than Windows/DOS ones ("\r\n").
- Do not leave debug
printf
s 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 emphasize 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.
- Do not use anonymous namespaces in 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.
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.
- Document public code with Doxygen comments, formatted as follows:
/// @brief Create a new grid of type @c GridType classified as a "Level Set",
/// i.e., a narrow-band level set.
///
/// @param voxelSize the size of a voxel in world units
/// @param halfWidth the half width of the narrow band in voxel units
///
/// @details The voxel size and the narrow band half width define the grid's
/// background value as halfWidth*voxelWidth. The transform is linear
/// with a uniform scaling only corresponding to the specified voxel size.
///
/// @note It is generally advisable to specify a half-width of the narrow band
/// that is larger than one voxel unit, otherwise zero crossings are not guaranteed.
template<typename GridType>
typename GridType::Ptr createLevelSet(
Real voxelSize = 1.0, Real halfWidth = LEVEL_SET_HALF_WIDTH);
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 global static constants instead. (Local static constants are not guaranteed to be initialized in a thread-safe manner.)
- 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.
- 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
wherever 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 if closely related.
- 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.