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-d.nosp@m.ev@li.nosp@m.sts..nosp@m.aswf..nosp@m.io.
Contents
Naming Conventions
Namespaces
- Lowercase words, keep simple and short (e.g., tools,tree).
- Use internal(ordetail) ormodule_internalfor 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 mprefix may be omitted for members ofstructs 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 ofGrid::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 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, andreturn.
- 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- 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 .hextension.
- 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 #includedirectives 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 .ccextension.
- 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 0and#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_toruint64_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 inlineand templates instead.
Classes
- Constructors that can be called with only one argument should be prefixed with the explicitkeyword 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 *thisin assignment operators.
- Declare methods as constwherever 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 usingdirectives (e.g.,using namespace std;).
- usingdeclarations 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 typenamerather thanclasswhen 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)orint(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.