VTK Coding Standards: Difference between revisions
Line 96: | Line 96: | ||
Example: | Example: | ||
<pre> | <pre> | ||
class vtkFoo | class vtkFoo : public vtkObject | ||
{ | { | ||
public: | public: |
Revision as of 13:23, 29 April 2009
We only have a few coding standards but they have proved very useful.
- We only put one public class per file. Some classes have helper classes that they use, but these are not accessible to the user.
- Every class, macro, etc starts with either vtk or VTK, this avoids name clashes with other libraries. Classes should all start with vtk and macros or constants can start with either.
- Class names and file names are the same. This makes it easier to find the correct file for a specific class.
- We only use alphanumeric characters in names, [a-zA-z0-9]. So names like Extract_Surface are not welcome. We use capitalization to indicate words within a name. For example ExtractVectorTopology could be an instance variable. If it were a class it would be called vtkExtractVectorTopology. We capitalize the first letter of a name (excluding any preceding vtk). For local variables almost anything goes. Ideally we would suggest using same convention as instance variables except start their names with a lower case letter. e.g. extractVectorSurface.
- We try to always spell out a name and not use abbreviations. This leads to longer names but it makes using the software easier because you know that the SetRasterFontRange method will always be called that, not SetRFRange or SetRFontRange or SetRFR. When the name includes a natural abbreviation such as OpenGL, we keep the abbreviation and capitalize the abbreviated letters.
- We try to keep all instance variables protected. The user and application developer should access instance variables through Set/Get methods. To aid in this there are a number of macros defined in vtkSetGet.h that can be used. They expand into inline functions that Set/Get the instance variable and invoke a Modified() method if the value has changed.
- Use "this" inside of methods even though C++ doesn't require you to. This really seems to make the code more readable because it disambiguates between instance variables and local or global variables. It also disambiguates between member functions and other functions.
- Do not use default argument values for C++ method parameters. When the method is wrapped for tcl, the method appears with however many parameters it has and is impossible to call without specifying the parameter values anyway. Rather, use method overloading to achieve the same effect, even in tcl. With overloading, you can have the signature that has all the required parameters and signatures with extra parameters and have both be callable from C++ or tcl. The implementation of one signature should be in terms of the other signature with the default values for the parameters encoded in the .cxx file method implementation.
- Make sure your code compiles without any warnings with -Wall and -O2.
- The indentation style can be characterized as the "indented brace" style. Indentations are two spaces, and the curly brace (scope delimiter) is placed on the following line and indented along with the code (i.e., the curly brace lines up with the code). Example:
if (this->Locator == locator) { return; } for (i = 0; i < this->Source->GetNumberOfPoints(); i++) { p1 = this->Source->GetPoint(i); [...] }
- The header file of the class should include only the superclass's header file. If you do not, the header test run as part of the VTK dashboard will report an error. If any other includes are absolutely necessary, include comment at each one describing why it should be included:
#include "vtkKWWindow.h" #include "vtkClientServerID.h" // Needed for InteractorID #include "vtkPVConfig.h" // Needed for PARAVIEW_USE_LOOKMARKS
- Avoid using vtkSetObjectMacro since it will require including the header file of another class. Use the vtkCxxSetObjectMacro instead. For example:
// Class declaration: // Description: // Set/Get the array used to store the visibility flags. virtual void SetVisibilityById(vtkUnsignedCharArray* vis);
// Cxx file vtkCxxSetObjectMacro(vtkStructuredVisibilityConstraint, VisibilityById, vtkUnsignedCharArray);
- All subclasses of vtkObject should include a PrintSelf() method that prints all publicly accessible ivars. For example:
void vtkObject::PrintSelf(ostream& os, vtkIndent indent) { os << indent << "Debug: " << (this->Debug ? "On\n" : "Off\n"); os << indent << "Modified Time: " << this->GetMTime() << "\n"; this->Superclass::PrintSelf(os, indent); os << indent << "Registered Events: "; if ( this->SubjectHelper ) { os << endl; this->SubjectHelper->PrintSelf(os,indent.GetNextIndent()); } else { os << "(none)\n"; } }
- All subclasses of vtkObject should include a type macro in their class declaration. For example:
class VTK_COMMON_EXPORT vtkBox : public vtkImplicitFunction { public: vtkTypeRevisionMacro(vtkBox,vtkImplicitFunction); void PrintSelf(ostream& os, vtkIndent indent); ... }
- If using STL, follow the guidelines specified in the VTK FAQ.
- When using anything declared in iostream, do not use std:: or vtkstd::. Examples include cerr, cout, ios... Do not include the iostream header. It is already included.
- Do not use 'id' as a variable name in public headers as it is a reserved word in Objective-C++.
Common Pitfalls
Set method in constructor
Set methods defined with vtkSetMacro cannot be used in the default constructor to initialize ivars because the first line of a Set method is to compare the current value of the ivar with the value in argument. As at this point (in the constructor), the ivar is not initialized yet, the comparison happens against an uninitialized value.
valgrind will detect this kind of fault.
For this reason, in the constructor, the value of an ivar has to be initialized directly with the assignment operator not through a Set method defined with vtkSetMacro.
Example:
class vtkFoo : public vtkObject { public: ... vtkGetMacro(X,int); vtkSetMacro(X,int); ... protected: vtkFoo(); int X; ... }; vtkFoo::vtkFoo { this->SetX(12); // neh } vtkFoo::vtkFoo { this->X=12; // good }
The issue looks pretty obvious in an isolated example like the one shown above. Sometimes it can also happen with an indirect call:
void SetXToZero() { this->SetX(0); } vtkFoo::vtkFoo { this->SetXToZero(); // neh }