Difference between revisions of "VTK Coding Standards"

From KitwarePublic
Jump to navigationJump to search
(Move some notes from the FAQ to the guidelines and add note on checking in STL header includes)
Line 78: Line 78:
  }
  }


* If using STL, follow the guidelines specified in the [[VTK FAQ#Can I use STL with VTK?|VTK FAQ]].
* STL usage:
** STL is for implementation, not interface. STL references should be contained in a .cxx class or the private section of the .h header file.
** Use the PIMPL idiom to forward reference/contain STL classes in heavily used superclasses. STL is big, fat, and slow to compile so we do not want to include STL headers in any .h files that are included by most of VTK, e.g. vtkObject.h vtkSource.h etc.
** Include the VTK wrapper header files: vtkstd/map instead of map.
** Use the vtkstd:: namespace to refer to STL classes and functions (except for iostreams as mentioned below)
** For an example of STL usage, see the [[VTK FAQ#Can I use STL with VTK?|VTK FAQ]].
** If you do need to include STL files within a VTK header, you must add a comment after it so that the automated repository commit checks will accept your changes:
  #include <vtksys/stl/vector> // STL Header <additional Comment here>


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

Revision as of 07:54, 13 September 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);
...
}
  • STL usage:
    • STL is for implementation, not interface. STL references should be contained in a .cxx class or the private section of the .h header file.
    • Use the PIMPL idiom to forward reference/contain STL classes in heavily used superclasses. STL is big, fat, and slow to compile so we do not want to include STL headers in any .h files that are included by most of VTK, e.g. vtkObject.h vtkSource.h etc.
    • Include the VTK wrapper header files: vtkstd/map instead of map.
    • Use the vtkstd:: namespace to refer to STL classes and functions (except for iostreams as mentioned below)
    • For an example of STL usage, see the VTK FAQ.
    • If you do need to include STL files within a VTK header, you must add a comment after it so that the automated repository commit checks will accept your changes:
 #include <vtksys/stl/vector> // STL Header <additional Comment here>
  • 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 vtkFoo::SetXToZero()
{
 this->SetX(0);
}

vtkFoo::vtkFoo
{
 this->SetXToZero(); // neh
}




VTK: [Welcome | Site Map]