TubeTK/Developers Guide

From KitwarePublic
Jump to navigationJump to search

TubeTK Developer's Guide

Created February 1, 2010.

Purpose

The following document is a description of the accepted coding style for TubeTK. Developers who wish to contribute code to TubeTK should read and adhere to the standards described here.

Document Overview

This document is organized into the following sections:

  • System Overview and Philosophy: Coding methodologies and motivation for the resulting style;
  • Copyright: The copyright header to be included in all files and other copyright issues;
  • File Organization: How to organize source code;
  • Naming Conventions: Patterns used to name classes, variables, template parameters, and instance variables;
  • Namespaces: The use of namespaces;
  • Code Layout and Indentation: Accepted standards for arranging code including indentation style;
  • Exception Handling: How to add exception handling to the system;
  • Documentation Style: A brief section describing the documentation philosophy used within TubeTK.

This style guide is an evolving document. Please confer with the TubeTK development team if you wish to add, modify, or delete the rules described in these guidelines.

Style Guidelines

The following coding-style guidelines are to be used in development of the TubeTK. To a large extent these guidelines are a result of the fundamental architectural and implementation decisions made early in the TubeTK project. For example, the decision was made to implement TubeTK with a C++ core using principles of generic programming, so the rules are oriented towards this style of implementation. Some guidelines are relatively arbitrary, such as indentation levels and style. However, an attempt was made to find coding styles consistent with accepted practices. The point is to adhere to a common style to assist developers and users of the future learn, use, maintain, and extend TubeTK.

Please do your best to be a upstanding member of the TubeTK team. The rules described here have been developed with the team as a whole in mind. If you consistently violate these rules you will likely be harassed mercilessly, first privately and then publicly. If this does not result in correct code layout, your right to Git write access (if you are developer and wish to contribute code) may be removed. Similarly, if you wish to contribute code and are not a developer, your code will not be accepted until the style is consistent with these guidelines.

System Overview and Philosophy

The following implementation strategies have been adopted by the TubeTK team. These directly and indirectly affect the resulting code style. Understanding these strategies motivate the reasons for many of the style guidelines described in this document.

Implementation Language

The core implementation language is C++. C++ was chosen for its flexibility, performance, and familiarity to team members. The toolkit uses the full spectrum of C++ features including const and volatile correctness, namespaces, partial template specialization, operator overloading, traits, and iterators.

Generic Programming and the STL

Compile-time binding using methods of generic programming and template instantiation is the preferred implementation style. This approach has demonstrated its ability to create efficient, flexible code. Use of the Standard Template Library (STL) is encouraged. In contrast with many libraries, STL containers are the acceptable for passing collections of data between public and private member functions. The STL is typically used by a class, rather than as serving as a base class for derivation of classes.

Portability

Most applications as well as the core of the toolkit are designed to compile on a set of target operating system/compiler combinations. These combinations are:

  • Linux with GCC;
  • Microsoft Windows XP with Microsoft Visual C++ (released within the past five years);
  • OS X with Clang or GCC;
  • Other Unix systems with GCC.

Some applications and modules make use of specific GPU configurations to optimize certain calculations. These vary according to system, but there shall always be an unoptimized implementation that will comply to the above portablity standards.

VTK and ITK

TubeTK makes extensive use of both the Visualization Toolkit (VTK) and the Insight Toolkit (ITK). Image processing features added to TubeTK should prefer ITK constructs over those of VTK. When visualization-geared code is written for TubeTK, VTK should be favored.

3D Slicer

TubeTK is intended to be use in conjunction with 3D Slicer, version 4. TubeTK Modules conform to the execution model used by the 3D Slicer project. For more information regarding bundling 3D Slicer extensions see the 3D Slicer documentation pages.

CMake Build Environment

The TubeTK build environment is CMake. CMake is an open-source, advanced cross-platform build system that enables developers to write simple makefiles (named CMakeLists.txt) that are processed to generated native build tools for a particular operating system/compiler combinations. See the CMake web pages at for more information.

Tool and Library Versions

Because of the agile nature of TubeTK developement, the TubeTK team uses developement versions of CMake, ITK, VTK, 3D Slicer, along with a recent stable version of Qt.

These are the most important factors influencing the coding style found in TubeTK. Now we will look at the details.

Copyright

File Organization

Classes are created and organized into a single class per file set. A file set consists of .h header file, .cxx implementation file, and/or a .hxx templated implementation file. Helper classes may also be defined in the file set, typically these are not visible to the system at large, or placed into a special namespace.

Source files must be placed in the correct directory for logical consistency with the rest of the system, and to avoid cyclic dependencies. The TubeTK source directory structure is divided into several directories below TubeTK. Please consult the TubeTK team before creating any new directories with TubeTK. Directories containing source are arranged as follows:

  • TubeTK/Applications: TubeTK/Applications consists of subdirectories for each TubeTK application. No application should depend upon another in any way;
  • TubeTK/Base;
  • TubeTK/SlicerModules.

Naming conventions

In general:

  • Names are constructed by using case change to indicate separate words, as in TimeStamp (versus Time_Stamp). Underscores are not used.
  • Variable names are chosen carefully with the intention to convey the meaning behind the code.
  • Names are generally spelled out; use of abbreviations is discouraged. (Abbreviation are allowable when in common use, and should be in uppercase as in RGB.) While this does result in long names, it self-documents the code. If you learn how to use name completion in your editor (e.g., vim or emacs), this inconvenience can be minimized.

Depending on whether the name is a class, file, variable, or other name, variations on this theme result as explained in the following subsections.

Naming Classes

Classes are named beginning with a capital letter. Classes are placed in the appropriate namespace, typically tube:: or itk::tube (see namespaces below). Classes are named according to the following general rule:

class name = <algorithm><input><concept>

In this formula, the name of the algorithm or process (possibly with an associated adjective or adverb) comes first, followed by an input type, and completed by a concept name. A concept is an informal classification describing what a class does. There are many concepts in TubeTK, here are a few of them:

  • Filter: A class that participates in the data processing pipeline. Filters typically take one or more inputs and produce one or more outputs;
  • Iterator: Traverse data in various ways (e.g., forward, backward, within a region, etc.);
  • Reader: A class that reads a single data object (e.g., image or mesh).
  • Region: A subset of a data object, such as an image region;
  • Source: A filter that initiates the data processing pipeline such as a reader or a procedural data generator;
  • Transform: Various types of transformations including affine and procedural;
  • Writer: A filter that terminates the data processing pipeline by writing data to disk or to a communications port.

The naming of classes is an art form; please review existing names to catch the spirit of the naming convention. Example names include:

  • ShrinkImageFilter;
  • TriangleCell;
  • ScalarImageRegionIterator;
  • NeighborhoodIterator;
  • MapContainer;
  • DefaultImageTraits;
  • BackwardDifferenceOperator.

Naming Modules

Modules created within TubeTK should be named in the same manner ascribed to other TubeTK classes. The directories that the modules are constructed in should end in Module as well as the shared library build of the module. However, the standalone version of the module should omit Module from its name.

Naming Files

Files should have the same name as the class, with the namespace of the library prepended (e.g., tube). Header files are named .h, while implementation files are named either .cxx or .hxx, depending on whether they are implementations of templated classes. It is important to note that VTK, ITK, and Qt classes fall under their respective namespaces and do not use the tube namespace.

Naming Methods and Functions

Global functions and class methods, either static or class members, are named beginning with a capital letter. The biggest challenge when naming methods and functions is to be consistent with existing names. For example, given the choice between ComputeBoundingBox( void ) and CalculateBoundingBox( void ) (CalcBoundingBox( void ) is not allowed because it is not spelled out), the choice is ComputeBoundingBox( void ) because Compute is used elsewhere in the system for a similar circumstance. The concepts described previously should be used whenever possible.

When referring to class methods and variables in code, an explicit this-> pointer should be used, as in this->ComputeBoundingBox(). The use of the explicit this-> pointer helps clarify exactly which method, and where it originates, is being invoked. Similarly the :: global namespace should be used when referring to a global function.

Naming Class Data Members

Class data members are prepended with m_ as in m_Size. This clearly indicates the origin of data members, and differentiates them from all other variables.

Naming Local Variables

Local variables begin in lowercase. There is more flexibility in the naming of local variables; please remember that others will study, maintain, fix, and extend your code. Any bread crumbs that you can drop in the way of explanatory variable names and comments will go a long way towards helping other developers.

Naming Template Parameters

Template parameters follow the usual rules with naming except that they should start with either the capital letter T or V. Type parameters begin with the letter T while value template parameters begin with the letter V.

Naming Typedefs

Typedefs are absolutely essential in generic programming. They significantly improve the readability of code, and facilitate the declaration of complex syntactic combinations. Unfortunately, creation of typedefs is tantamount to creating another programming language. Hence typedefs must be used in a consistent fashion.

The general rule for typedef names is that they end in the word Type. For example:

 typedef TPixel PixelType;

However, there are many exceptions to this rule that recognize that TubeTK has several important concepts that are expressed partially in the names used to implement the concept. An iterator is a concept, as is a container or pointer. These concepts are used in preference to Type at the end of a typedef as appropriate. For example:

 typedef typename ImageTraits::PixelContainer PixelContainer;

Here Container is a concept used in place of Type.

Using Underscores

Do not use them. The only exception is when defining preprocessor variables and macros (which are discouraged). In this case, underscores are allowed to separate words.

Preprocessor Directives

Some of the worst code contains many preprocessor directives and macros. Do not use them except in a very limited sense (to support minor differences in compilers or operating systems). If a method makes extensive use of preprocessor directives, it is a candidate for separation into its own class.

Unused Parameters and Shadowed Parameters

Use the macros vtkNotUsed() and itkNotUsed() when implementing class member functions that have unused parameters. Warnings are treated as errors, and unused parameters will generate warnings.

void SetSigma( double sigma, double itkNotUsed( extent ) )
{
  ...some code that does not use the parameter extent...
}

Shadowed parameters are particularly troublesome in that they often indicate code errors and ambiguities. Again, warnings are treated as errors, and shadowed variables will generate warnings.

A common mistake is to give a variable larger scope than what it needs. To avoid this, define loop variables in the loop declaration:

for( unsigned int i = 0; i < 100; ++i )
  {
  ...loop stuff...
  }

Following our naming conventions also helps avoid shadowed variables. In particular, make sure all member variables begin with m_, functions begin with uppercase variables, and variables names are descriptive.

Hidden Virtual Functions

If a base class declares a function, then an instance of that function overloaded in subclasses must include a definition that has the same parameter arguments (signature). For example, if the base class function takes two doubles as arguments, and the derived class has an instance that takes only one double as an argument, then a warning (i.e., an error) is generated. You must also define an instance in the derived class that takes two arguments.

New Line at End of File

Some compilers will complain (generate a warning/error) if a file does not end with a new line. To be safe, make sure each file ends with a blank line.

Namespaces

All classes that do not inherit from VTK, ITK, or Qt classes should be placed in the tube namespace.

Const Correctness

Const correctness is important. Please use it as appropriate to your class or method.

Exception Handling

Indicate that methods throw exceptions in the method declaration as in:

 const float * foo( void ) const throws std::exception

Code Layout and Indentation

The following are the accepted TubeTK code layout rules and indentation style. After reading this section, you may wish to visit many of the source files found in ITK. This will help crystallize the rules described here.

General Layout

Each line of code should take no more than 80 characters. Break the code across multiple lines as necessary. Use lots of whitespace to separate logical blocks of code, intermixed with comments. To a large extent the structure of code directly expresses its implementation.

Indentation and Tabs

The appropriate indentation level is two spaces for each level of indentation. Do not use tabs. Set up your editor to insert spaces. Using tabs may look good in your editor but will wreak havoc in someone else's.

VisualStudioTabsSpaces.png

Declarations

The declaration of variables within classes, methods, and functions should be one declaration per line.

 int    i;
 int    j;
 char * stringname;

Spaces

TubeTK, like ITK, makes liberal use of spaces to improve the readability of code. When in doubt, use a space. Common examples:

  • Separate expressions from their enclosing paren:
 if( a < b )
   {
   a = ( 1 + 1 ) / 2;
   }
  • Separate arguments from their enclosing paren and from other args (there should be a space after every comma):
 foo( int a )
 {
   foofoo( a );
   foofoofoo( a, b );
 }
  • Favor right-flush for lines that are continuations of previous lines:
 this->IsMyReallyLong()->ITK()
                       ->FunctionCall()
  • Use ( void ) when declaring a function that takes no arguments:
 int DoesNothing( void )
 {
 }
  • Use spaces before and after raw pointer declarations:
 int * a;
  • You do not need to use spaces when accessing raw pointers:
 a->b;
 *a = b;

Keep It Simple

Break complex methods into multiple lines. Program in a way that makes it easy for your code to be debugged by others. Examples:

  • ?: statements should not be used instead of if...then statements;
  • Limit the variety of operators that appear in any one line of code.

Includes

  • Only include the necessary files, no more;
  • Group includes per library;
  • Alphabetically sort files within groups;
  • Order groups from local to global;
  • Implementation files should include the header files first.

Class Layout

Classes are defined using the following guidelines:

  • Begin with #include guards;
  • Follow with the necessary includes. Include only what is necessary to avoid dependency problems;
  • Place the class in the correct namespace;
  • Public methods come first;
  • Protected methods follow;
  • Private members come last;
  • Public data members are forbidden.

The class layout looks something like this:

 /*=========================================================================
 
 Library:   TubeTK
 
 Copyright 2010 Kitware Inc. 28 Corporate Drive,
 Clifton Park, NY, 12065, USA.
 
 All rights reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
     http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 
 =========================================================================*/
 
 #ifndef __itktubeExampleImage_h
 #define __itktubeExampleImage_h
  
 #include <itkDefaultDataAccessor.h>
 #include <itkDefaultImageTraits.h>
 #include <itkImageBase.h>
 #include <itkPixelTraits.h>
 
 namespace itk
 {
  
 namespace tube
 {
  
 template< class TPixel, unsigned int VImageDimension = 2,
           class TImageTraits = DefaultImageTraits< TPixel, VImageDimension > >
 class ITK_EXPORT ExampleImage : public ImageBase< VImageDimension >
 {
 public:
   typedef ExampleImage                 Self;
   typedef ImageBase< VImageDimension > Superclass;
   typedef SmartPointer< Self >         Pointer;
   typedef SmartPointer< const Self >   ConstPointer;
 
   itkNewMacro( Self );
   itkTypeMacro( ExampleImage, ImageBase );
 
   ....

 protected:    
   ExampleImage( void );
 
   virtual ~ExampleImage( void )
     {
     }
 
   ....

 private:
   ExampleImage( const Self & other );
 
   void operator=( const Self & other );
 
   ....
 
   PixelContainerPointer m_Buffer
 
   ....
 
 }; // End class ExampleImage
 
 } // End namespace tube
 
 } // End namespace itk
 
 #endif // End !defined(__itktubeExampleImage_h)

Method Definitions

Methods are defined across multiple lines. This is to accommodate the extremely long definitions possible when using templates. The starting and ending brace should be indented. For example:

 template< class TPixel, unsigned int VImageDimension, 
           class TImageTraits >
 const double * 
 ExampleImage< TPixel, VImageDimension, TImageTraits >
 ::GetSpacing( void ) const
 {
   ...
 }

The first line is the template declaration. The second line is the method return type. The third line is the class qualifier. And the fourth line in the example above is the name of the method.

Use of Braces { }

Braces must be used to delimit the scope of an if, for, while, switch, or other control structure. Braces are placed on a line by themselves:

 for( unsigned int i = 0; i < 3; ++i )
   {
   ...
   }

or when using an if:

 if( condition )
   {
   ...
   }
 else if( other condition )
   {
   ...
   }
 else
   {
   ....
   }

Doxygen Documentation System

Doxygen is an open-source system for automatically generating documentation from source code. To use Doxygen effectively, the developer must insert comments, delimited in a special way, that Doxygen extracts to produce the documentation. While there are a large number of options to Doxygen, developers at a minimum should insert the following Doxygen commands.

Documenting a Class

Classes should be documented using the \class and \brief Doxygen commands, followed by the detailed class description:

 /**
  * \class Object
  * \brief Base class for most itk classes.
  *
  * Object is the second-highest level base class for most itk objects.
  * It extends the base object functionality of LightObject by
  * implementing debug flags/methods and modification time tracking.
  */

Documenting a Method

Methods should be documented using the following comment block style as shown in the following example:

 /**
  * Access a pixel. This version can be an lvalue.
  */
 TPixel & operator[]( const IndexType & index )
 { 
   return this->GetPixel( index ); 
 }

The key here is that the comment starts with /**, each subsequent line has an aligned *, and the comment block terminates with a */.

Testing

Each developer should do his or her part to aid in testing process. It is generally accepted that each developer should write proper unit tests using CTest and those tests will be run on nightly and continuous dashboards.

Dashboards

  • CDash Dashboards are maintained for all platforms;
  • Warnings are not tolerated;
  • Failing tests are preferred over disabling tests or using case/system-specific hacks for solutions. The best answer is to fix the code.

Releases

Before each product release, a battery of testing (specified on the project wiki) will be carried out to check for regressions not caught by traditional unit testing. Release version naming convention:

  • MajorReleaseNumber.MinorReleaseNumber.PatchReleaseNumber;
  • These variables are controlled in the top-level CMakeLists.txt file. They apply to all applications within TubeTK.

Code Coverage

Code coverage is responsibility of all developers.

Memory Leaks

Memory leaks must be plugged. A list of leaks and memory defects can be found on the Dashboard under the Dynamic Analysis section. Clicking on the Defect Count will show further information.

A script is available for Linux and OS X to help debug the memory leaks. It can generate the same output present on the dashboard on demand for a subset of tests. The command

./TubeTK/Utilities/Valgrind/runValgrind.sh -h

shows full usage instructions. Essentially install valgrind and xsltproc, then execute the runValgrind.sh script from TubeTK-Build with arguments such as

-R leakyTubeTestName.

Git repo management

Create a fork in your github account, and perform pull requests.

Optimizing code in Linux