Coding Conventions#
General#
VTK is a large body of code with many users and developers. Coding in a consistent style eases shared development. VTK’s style guidelines also ensure wide portability. All code that is contributed to VTK must conform to the following style guidelines. Exceptions are permissible, following discussion in code review, as long as the result passes the nightly regression tests. External code contributed into the ThirdParty directory is exempt from most of the following rules except for the rules that say “All code”.
All code that is compiled into VTK by default must be compatible with VTK’s BSD- style license.
Copyright notices should appear at the top of C++ header and implementation files using SPDX syntax.
All C++ code must be valid C++17 code.
Multiple inheritance is not allowed in VTK classes.
Rationale: One important reason is that Java does not support it.
Only one public class per header file. Internal helper classes may be forward declared in header files, but can then only be defined in implementation files, ie using the PIMPL idiom.
Rationale: helpful when searching the code and limits header inclusion bloat that slows compilation time.
Class names and file names must match, class names must be unique.
Rationale: helpful when searching the code, includes are flattened at install.
The indentation style used is a modified version of the Allman style. Indentations consist of two spaces, and tabs are not permitted. Additionally, trailing whitespace is not allowed. Curly braces, which serve as scope delimiters, are placed on the line following the control statement and are indented to the same level as that statement.
Rationale: Readability and historical, Removing tabs ensures that blocks are indented consistently in all editors.
Conditional clauses (including loop conditionals such as for and while) must be in braces below the conditional. Ie, instead of
if (test) clauseorif (test) { clause }, useif (test) { clause }
Rationale: helpful when running code through a debugger
Init-statement in a condition like
if ( int i = 0; ... )is allowed except for modifying an existing variable outside the conditional clause. Rationale: ReadabilityOnly alphanumeric characters in names. Use capitalization to demarcate words within a name (i.e., camel case). Use pascal case for
staticvariables. Preprocessor variables are the exception, and should be in all caps with a single underscore to demarcate words.Rationale: Readability
Every class, macro, etc starts with either vtk or VTK. Classes should all start with lowercase vtk and macros or constants can start with either.
Rationale: avoids name clashes with other libraries
Every free function should be in the “vtk” namespace.
Rationale: Namespace symbol collision avoidance
After the
vtkprefix, capitalize the first letter of class names, methods and static and instance variables. Local variables are allowed to vary, but ideally should start in lower case and then proceed in camel case.Rationale: Readability
Try to always spell out a name and not use abbreviations except in cases where the shortened form is obvious and widely understood.
Rationale: Readability, self-documentation
Classes that derive from
vtkObjectshould have protected constructors and destructors, and privately declared but unimplemented copy constructor and assignment operator.Classes that don’t derive from
vtkObjectshould obey the rule of three. If the class implements the destructor, copy constructor or copy assignment operator they should implement all of them.
Rationale: VTK’s reference counting implementation depends on carefully controlling each object’s lifetime.
Following the copyright notice, the name and purpose of each class should be documented at the top of the header with standard doxygen markup.:
/** * @class vtkclassname * @brief one line description * * Longer description of class here. */
Rationale: Doxygen generated documentation uses this to describe each class.
Public methods must be documented with doxygen markup.
/** * Explanation of what the method/ivar is for */
Descriptions should do more than simply restate the method or ivar’s name.
The documentation for each public ivar should document the default value.
The documentation style for SetGet macros should be a single comment for the pair and a brief description of the variable that is being set/get. Use doxygen group marking to make the comment apply to both macro expanded functions.
///@{ /** * Set / get the sharpness of decay of the splats. * This is the exponent constant in the Gaussian * equation. Normally this is a negative value. */ */ vtkSetMacro(ExponentFactor,double); vtkGetMacro(ExponentFactor,double); ///@}
The documentation style for vector macros is to name each of the resulting variables. For example comment
/** * Set/Get the color which is used to draw shapes in the image. The parameters are SetDrawColor(red, green, blue, alpha) */ vtkSetVector4Macro(DrawColor, double); vtkGetVector4Macro(DrawColor, double);
The description for SetClamp macros must describe the valid range of values.
/** * Should the data with value 0 be ignored? Valid range (0, 1). */ vtkSetClampMacro(IgnoreZero, int, 0, 1); vtkGetMacro(IgnoreZero, int);
Rationale: Doxygen generated documentation (http://www.vtk.org/doc/nightly/html/) is generated from these comments and should be consistently readable.
Public and even Protected instance variables are allowed only in exceptional situations. Private variables should be used instead with public access given via Set/Get macro methods when needed. Rationale: Consistent API, ease of deprecation, and SetMacro takes part in reference counting.
Protected methods are allowed only when they are intended to be used by inheriting classes and overridden by inheriting classes. Private methods should be the default for any method. Please note this is not true in many classes but should be followed when adding new code. Rationale: Consistent API, ease of deprecation.
Accessors to
vtkObjectinstance variables should be declared in the header file, and defined in the implementation file with the vtkCxxSetObjectMacro. Rationale: Reduces header file bloat and assists in reference counting.Use
this->inside of methods to access class methods and instance variables. Rationale: Readability as it helps to distinguish local variables from instance variables.Header files should normally have just two includes, one for the superclass’ header file and one for the class’ module export header declaration. It is required that all but the superclass header have a comment explaining why the extra includes are necessary. Care should be taken to minimize the number of includes in public headers, with predeclaration/PIMPL preferred. Rationale: limits header inclusion bloat that slows compilation time.
Include statements in implementation files should generally be in alphabetical order, grouped by type. For example, VTK includes first, system includes, STL includes, and Qt includes. Rationale: avoid redundant includes, and keep a logical order.
All subclasses of
vtkObjectshould include aPrintSelf()method that prints all publicly accessible ivars.Rationale: useful in debugging and in wrapped languages that lack sufficient introspection.
All subclasses of
vtkObjectshould include a type macro in their class declaration.Rationale: VTK’s implementation of runtime type information depends on it
Do not use
idas a variable name in public headers, also avoidmin,max, and other symbols that conflict with the Windows API.Rationale:
idis a reserved word in Objective-C++, and against variable name rules.min,max, and less common identifiers listed in Testing/Core/WindowsMangleList.py are declared in the Windows API.Prefer the use of vtkNew when the variable would be classically treated as a stack variable.
Eighty character line width is preferred. More than one hundred is not allowed.
Rationale: Readability
Method definitions in implementation files should be preceded by // followed by 78
-characters.Rationale: Readability
New code must include regression tests that will run on the dashboards. The name of the file to test vtkClassName should be TestClassName.cxx. Each test should call several functions, each as short as possible, to exercise a specific functionality of the class. The
main()function of the test file must be called TestClassName(int, char*[])Rationale: Code that is not tested can not be said to be working.
All code must compile and run without warning or error messages on the nightly dashboards, which include Windows, Mac, Linux and Unix machines. Exceptions can be made, for example to exclude warnings from ThirdParty libraries, by adding exceptions to CMake/CTestCustom.cmake.in
Namespaces should not be brought into global scope in any public headers, i.e. the
usingkeyword should not appear in any public headers except within class scope. It can be used in implementations, but it is preferred to bring symbols into the global scope rather than an entire namespace.Rationale: Using VTK API should not have side-effects where parts of the std namespace (or the entire thing) are suddenly moved to global scope.
While much of the legacy VTK API uses integers for boolean values, new interfaces should prefer the bool type.
Rationale: Readability.
Template classes are permitted, but must be excluded from wrapped languages.
Rationale: The concept of templates doesn’t exist in all wrapped languages.
Prefer overloading functions to default arguments.
Rationale: Default function arguments in C++ are a tempting way to add an argument to a function while maintaining easy backwards compatibility. However, if you later want to add another argument to the list in a way that preserves backwards compatibility, it, too, must be a default argument. To supply the second of these arguments in a call forces you to also supply the first argument, even if it is the default value. As a result, this is not a clean way to add a argument to a function. Instead, function overloading should be preferred.
VTK classes (header and source file) must be defined in the VTK_ABI_NAMESPACE, using VTK_ABI_NAMESPACE_BEGIN and VTK_ABI_NAMESPACE_END at the deepest namespace level, if any.
VTK_ABI_NAMESPACE_BEGIN class VTKXXX_EXPORT vtkXXX : public vtkObject{/* ... */}; VTK_ABI_NAMESPACE_END namespace vtk { VTK_ABI_NAMESPACE_BEGIN void VTKXXX_EXPORT yyy(); VTK_ABI_NAMESPACE_END }
Removing of public and even protected members and methods should follow deprecation policy.
Rationale: Consistent API,
Fully unused arguments in functions and methods should not be omitted but instead should be hidden using
vtkNotUsed(arg).Rationale: Readability.
Specific C++ Language Guidelines#
C++ Standard Library#
Do not use vtkStdString in new API; prefer std::string
Rationale: vtkStdString was introduced as a workaround for compilers that couldn’t handle the long symbol name for the expanded std::string type. It is no longer needed on modern platforms.
STL usage in the Common modules’ public API is discouraged when possible, Common modules are free to use STL in implementation files. The other modules may use STL, but should do so only when necessary if there is not an appropriate VTK class.
Rationale: limits header inclusion bloat, wrappers are not capable of handling many non-
vtkObjectderived classes.
C++ Language Features Required when using VTK#
C++ Features allowed throughout VTK#
default The use of default is encouraged in preference to empty destructor implementations
static_assert Must use the static_assert (
bool_constexpr) signature.strongly typed enums VTK prefers the usage of strongly typed enums over classic weakly typed enums.
Weakly typed enums conversion to integers is undesirable, and the ability for strongly typed enums to specify explicit storage size make it the preferred form of enums.
strongly typed:
enum class Color { red, blue };weakly typed:
enum Color { red, blue };While VTK is aware that conversion of all enums over to strongly typed enums will uncover a collection of subtle faults and incorrect assumptions. Converting existing classes to use strongly typed enums will need to be investigated and discussed with the mailing list, as this will break API/ABI, potentially cause issues with VTK bindings, and possibly require changes to users VTK code.
attributes The use of attribute is encouraged and should be used wisely depending on the attribute:
fallthough should be used instead of
VTK_FALLTHROUGH.maybe_unused should be used.
nodiscard should be used.
std::optional should be used for returned value of method to simplify method signature and to factorize code.
std::string_view should be used to avoid string copy especially for IO treatment.
class template argument deduction
Usage of class template argument deduction can be used for trivial type deduction like:
std::pair p(2, 4.5); // deduces to std::pair<int, double> p(2, 4.5);
C++ Features acceptable in VTK implementation files, private headers, and template implementations#
braced initializer list Braced initializer list are allowed as they prevent implicit narrowing conversions, and “most vexing parse” errors. They can be used when constructing POD’s and other containers.
Braced initializer lists are not allowed to be used as the right hand side for auto:
auto a = { 10, 20 }; //not allowed as a is std::initializer_list<int>
-
Do not combine shared_ptr and vtk derived objects. VTK internal reference counting makes the shared_ptr reference counting ( and destructor tracking ) pointless.
-
The use of alias templates is preferred over using ‘typedefs’. They provide the same language pattern of normal declarations, and reduce the need for helper template structs. For example ( Scott Meyers, Effective Modern C++ )
template<typename T> using MyAllocList = std::list<T, MyAlloc<T>>;
universal references (&&) / std::move / std::forward
-
Used in the
vtkDataArrayfor example.
-
The use of std::array is preferred over using raw fixed sized arrays. They offer compile time bounds checking without any runtime cost.
std::variant can be used in internal API.
C++ Features allowed under certain conditions#
-
Concurrency inside of vtk should be handled by using or extending the already existing collection of support classes like vtkAtomic and vtkSMPThreadLocal.
Instead of directly using new c++11 constructs such as std::compare_exchange_weak instead extend the functionality of vtk core concurrency classes.
Note: Thread local storage has not been supported on OSX previously to XCode 8. VTK offers the following classes that should be used instead:
std::isnan, std::isfinite, std::isinf
These functions should not be called directly, instead the wrapped versions provided by vtk should be used instead.
vtk::isnan -> std::isnan
vtk::isfinite -> std::isfinite
vtk::isisinfnan -> std::isinf
The reason for these wrappings is to work around compiler performance issues. For example, some clang version would convert integral types to double and do the operation on the double value, instead of simply returning false/true.
-
Future/Async based programming inside of vtk should be handled on a case by case basis. In general the use cases for this kind of execution model is best applied at the vtkExecutive / vtkPipeline level, or at the File IO level.
In these cases the recommendation is to extending or adding support classes so that these design patterns can be utilized in the future.
constexpr should be used for constant (instead of
#define,const ...) and wisely used for function depending on your use case.auto Use auto to avoid type names that are noisy, obvious, or unimportant - cases where the type doesn’t aid in clarity for the reader. auto is permitted when it increases readability, particularly as described below. Never initialize an auto-typed variable with a braced initializer list.
Specific cases where auto is allowed or encouraged:
(Encouraged) For iterators and other long/convoluted type names, particularly when the type is clear from context (calls to find, begin, or end for instance).
(Allowed) When the type is clear from local context (in the same expression or within a few lines). Initialization of a pointer or smart pointer with calls to new commonly falls into this category, as does use of auto in a range-based loop over a container whose type is spelled out nearby.
(Allowed) When the type doesn’t matter because it isn’t being used for anything other than equality comparison.
(Encouraged) When iterating over a map with a range-based loop (because it is often assumed that the correct type is std::pair<KeyType, ValueType> whereas it is actually std::pair<const KeyType, ValueType>). This is particularly well paired with local key and value aliases for .first and .second (often const-ref).
for (const auto& [key, value] : some_map) { // The rest of the loop can now just refer to key and value, // a reader can see the types in question, and we've avoided // the too-common case of extra copies in this iteration. }
(Allowed) structure binding can be used for trivial type deduction.
(Prohibited) When iterating in integer space.
for (auto i=0; i < grid->GetNumberOfPoints(); ++i). Because vtk data structures usually contain more than 2 billion elements, iterating using 32bit integer is prohibited in these use case (and often doesn’t match the type used).
-
Do not use unique_ptr with vtk-derived objects. We recommend using
vtkNewbecause VTK objects have their own internal reference counting and custom deletion logic, making unique_ptr’s ownership semantics invalid.std::unique_ptrshould be created withmake_unique.
-
Usage of lambda expressions are allowed with the following guidelines.
Use default capture by value ([=]) only as a means of binding a few variables for a short lambda, where the set of captured variables is obvious at a glance. Prefer not to write long or complex lambdas with default capture by value.
All capture arguments must be explicitly defined. Using the default capture by reference ([&]) is not permitted. This ensures easier evaluation of lifespan and reference ownership.
lambda expression should be
constexpr.Keep unnamed lambdas short. If a lambda body is more than maybe five lines long, prefer using a named function instead of a lambda.
Specify the return type of the lambda explicitly if that will make it more obvious to readers.
variadic templates and fold expression.
Variadic Templates and fold expression are not allowed in VTK unless they are the only solution to the given problem (e.g.
vtkArrayDispatch.txx).
VTK’s commit hook enforced style checks#
Well formed commit message
Every commit message should consist of a one line summary optionally followed by a blank line and further details. This is most easily approximated to the subject of an email, and the body in the form of paragraphs.
Valid committer username and email address Every developer must have a valid name and email configured in git.\
ASCII filename check All file names must contain only ASCII characters.
No tabs
No trailing whitespace
No empty line at end of file
Proper file access mode
Files must be committed with sensible access modes.
One megabyte maximum file size
No submodules
For required third party dependencies, the recommended scheme is to use git’s subtree merge strategy to reproducibly import code and thereby simplify eventual integration of upstream changes. Copies of these third party libraries with branches specifically for vendoring them in VTK are located in the third-party GitLab space.
Additionally, new developers should be aware that the regression test machines have fairly strict compiler warnings enabled and usually have VTK_DEBUG_LEAKS configured on to catch leaks of VTK objects. Developers should be in the habit of doing the same in their own environments so as to avoid pushing code that the dashboards will immediately object to. With GCC, it is easiest to do so by turning on VTK_EXTRA_COMPILER_WARNINGS.