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”.

  1. All code that is compiled into VTK by default must be compatible with VTK’s BSD- style license.

  2. Copyright notices should appear at the top of C++ header and implementation files using SPDX syntax.

  3. All C++ code must be valid C++17 code.

  4. Multiple inheritance is not allowed in VTK classes.

    Rationale: One important reason is that Java does not support it.

  5. 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.

  6. Class names and file names must match, class names must be unique.

    Rationale: helpful when searching the code, includes are flattened at install.

  7. 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.

  8. Conditional clauses (including loop conditionals such as for and while) must be in braces below the conditional. Ie, instead of if (test) clause or if (test) { clause }, use

    if (test)
    {
      clause
    }
    

    Rationale: helpful when running code through a debugger

  9. Init-statement in a condition like if ( int i = 0; ... ) is allowed except for modifying an existing variable outside the conditional clause. Rationale: Readability

  10. Only alphanumeric characters in names. Use capitalization to demarcate words within a name (i.e., camel case). Use pascal case for static variables. Preprocessor variables are the exception, and should be in all caps with a single underscore to demarcate words.

    Rationale: Readability

  11. 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

  12. Every free function should be in the “vtk” namespace.

    Rationale: Namespace symbol collision avoidance

  13. After the vtk prefix, 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

  14. 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

  15. Classes that derive from vtkObject should have protected constructors and destructors, and privately declared but unimplemented copy constructor and assignment operator.

    1. Classes that don’t derive from vtkObject should 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.

  16. 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.

  17. 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.

  18. 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.

  19. 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.

  20. Accessors to vtkObject instance 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.

  21. Use this-> inside of methods to access class methods and instance variables. Rationale: Readability as it helps to distinguish local variables from instance variables.

  22. 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.

  23. 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.

  24. All subclasses of vtkObject should include a PrintSelf() method that prints all publicly accessible ivars.

    Rationale: useful in debugging and in wrapped languages that lack sufficient introspection.

  25. All subclasses of vtkObject should include a type macro in their class declaration.

    Rationale: VTK’s implementation of runtime type information depends on it

  26. Do not use id as a variable name in public headers, also avoid min, max, and other symbols that conflict with the Windows API.

    Rationale: id is 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.

  27. Prefer the use of vtkNew when the variable would be classically treated as a stack variable.

  28. Eighty character line width is preferred. More than one hundred is not allowed.

    Rationale: Readability

  29. Method definitions in implementation files should be preceded by // followed by 78 - characters.

    Rationale: Readability

  30. 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.

  31. 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

  32. Namespaces should not be brought into global scope in any public headers, i.e. the using keyword 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.

  33. While much of the legacy VTK API uses integers for boolean values, new interfaces should prefer the bool type.

    Rationale: Readability.

  34. Template classes are permitted, but must be excluded from wrapped languages.

    Rationale: The concept of templates doesn’t exist in all wrapped languages.

  35. 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.

  36. 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
    }
    
  37. Removing of public and even protected members and methods should follow deprecation policy.

    Rationale: Consistent API,

  38. 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-vtkObject derived classes.

C++ Language Features Required when using VTK#

  • nullptr Use nullptr instead of 0 and NULL when dealing with pointer types

  • override VTK_OVERRIDE will be replaced with the override keyword

  • final VTK_FINAL will be replaced with the final keyword

  • delete The use of delete is preferred over making default members private and unimplemented.

  • non static data member initializers

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:

  • 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>
    
  • shared_ptr

    • Do not combine shared_ptr and vtk derived objects. VTK internal reference counting makes the shared_ptr reference counting ( and destructor tracking ) pointless.

  • template alias

    • 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

  • extern templates

    • Used in the vtkDataArray for example.

  • unordered maps

  • std::array

    • The use of std::array is preferred over using raw fixed sized arrays. They offer compile time bounds checking without any runtime cost.

  • range based for loop

  • std::variant can be used in internal API.

C++ Features allowed under certain conditions#

  • concurrency

    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.

  • std::future/ std::async

    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).

  • unique_ptr

    • Do not use unique_ptr with vtk-derived objects. We recommend using vtkNew because VTK objects have their own internal reference counting and custom deletion logic, making unique_ptr’s ownership semantics invalid.

    • std::unique_ptr should be created with make_unique.

  • lambda expressions

    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.