Maverick/Developers Guide: Difference between revisions
(New page: <em> Back to AFRL </em> = Maverick Developer's Guide= * PDF version of this document ** Created Aug 18th, 2008 = Purpose= The...) |
|||
(2 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
= Purpose= | = Purpose= |
Latest revision as of 02:14, 15 September 2009
Purpose
The following document is a description of the accepted coding style for the Maverick Toolkit. Developers who wish to contribute code to Maverick should read and adhere to the standards described here.
Document Overview
This document is organized into the following sections.
- System Overview \& 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 Maverick.
This style guide is an evolving document. Please confer with the Maverick 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 Maverick Toolkit. To a large extent these guidelines are a result of the fundamental architectural and implementation decisions made early in the Maverick project. For example, the decision was made to implement Maverick 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 the Maverick Toolkit.
Please do your best to be a upstanding member of the Maverick 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 SVN 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 & Philosophy
The following implementation strategies have been adopted by the Maverick 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 Maverick 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 STL (Standard Template Library) 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 {\em used} by a class, rather than as serving as a base class for derivation of Maverick classes.
Portability
Most Maverick 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:
- Windows XP with Microsoft Visual C++ (Released within the past 5 years.)
- Linux with GCC
- Mac OSX 10.4 (Tiger) and 10.5 (Leopard) with GCC
- Other *nix systems with GCC
Some Maverick 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 Based
Maverick makes extensive use of both the Visualization Toolkit (VTK) and the Insight Toolkit (ITK). Image processing features added to Maverick should prefer ITK constructs over those of VTK. When visualization-geared code is written for Maverick, VTK should be favored. All Maverick code is considered proprietary for the duration of the AFRL project at which point all VTK and ITK classes will be reviewed for possible contribution to the community.
Slicer3 Module Architecture
One of the strengths of Maverick is its modular architecture. Maverick allows for the inclusion of modules in any Maverick application. This feature is achieved through the execution model used by the Slicer3 project. Though the Maverick team makes every effort to conform to the Slicer3 Module standard some modules may need to be modified to conform to the standard. For more information regarding constructing Slicer3 modules see:
http://www.na-mic.org/Wiki/index.php/Slicer3:Execution_Model_Documentation
Slicer3 MRML
Maverick makes use of MRML as its standard format for storage of scene elements and exchange between Maverick applications. It is very important to note that Maverick only supports a subset of the full MRML spec. MRML access is generally restricted to VisualObject classes.
Qt as the GUI Framework
Another strength of Maverick is the ease of assembling applications with fully-featured Graphical User Interfaces (GUIs). This is done by using and extending Qt classes to created widgets unique to the Maverick toolkit. Any Maverick widget is expected to have a corresponding plugin such that the majority of the user interface can be built in the Qt Designer.
CMake Build Environment
The Maverick 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 http://www.cmake.org for more information.
Tool and Library Versions
Because of the agile nature of Maverick toolkit developement, the Maverick team uses the latest developement versions of VTK, ITK, Slicer3, and CMake along with the latest stable version of Qt. This becomes an unrealistic requirement for outside developers; thus, each Maverick release will require a specific version of the listed libraries and tools.
These are the most important factors influencing the coding style found in Insight. Now we will look at the details.
Copyright
Maverick is a closed-source product. This copyright should be placed at the head of every source code file. The current copyright reads as follows:
/*========================================================================= Program: Maverick Module: $RCSfile: config.h,v $ Copyright (c) Kitware Inc. 28 Corporate Drive, Clifton Park, NY, 12065, USA. All rights reserved. No part of this software may be reproduced, distributed, or modified, in any form or by any means, without permission in writing from Kitware Inc. IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. =========================================================================*/
This header is available at the top level of Maverick in the file Header.txt
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 .txx 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 Maverick source directory structure is divided into several directories below \texttt{Maverick}. Please consult the Maverick team before creating any new directories with Maverick. Directories containing source are arranged as follows:
Maverick/Application
Maverick/Applications consists of subdirectories for each Maverick application. Experimental Applications should be placed in the Experimental folder, and no application should depend upon another in any way.
Maverick/Code
Maverick/Code should contain no Qt-based code and consists of the following sub-directories:
- IO --- Reading/writing files from disk or databases
- Numerics --- core numeric functions and classes (Functions and Calculators)
- Common --- core classes to Maverick including the VisualObject and Scene classes (mav Nodes)
- BasicFilters --- common image processing and mesh algorithms (Filters)
- VTK --- classes that inherit from or heavily utilize VTK. They are mostly for Maverick-specific visualization
- Paintbrushes --- classes that implement intelligent paint brushes
- MRML --- MRML nodes unique to Maverick
Dependencies
File:Maverick Code Dependencies.jpg
Maverick/Examples
Maverick/Examples contains subdirectories for various examples. When adding a fairly complex widget or feature to Maverick, you are encouraged to add an example that exhibits its behavior. Examples are a lot more useful than conventional documentation in the eyes of many developers.
Maverick/Libs
Maverick/Libs consists of the following subdirectories:
- VTKEdge --- paintbrush base classes and advance rendering capabilities
- Slicer --- library containing Maverick-specific changes to existing Slicer3 code
- VolViewPreset --- library containing custom color-transfer functions from Kitware's VolView product.
- WorkflowLibrary --- library containing workflow code used in some Maverick applications.
Maverick/Modules
Maverick/Modules contains subdirectories for each Slicer3 module built for Maverick.
Maverick/QmModuleGUI
Maverick/QmModuleGUI contains the Qt wrappers for the Modules created in Maverick/Modules. These wrappers and their corresponding plugins allow Slicer3 and Maverick modules to be added to any Maverick application using the Qt Designer and a minimum of function calls.
Maverick/QmWidgets
Maverick/QmWidgets contains all Qt Widgets written for use in the Maverick toolkit. These widgets and their respective plugins allow Maverick applications to be built quickly using the Qt Designer.
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), 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 mav:: (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 {\em concept} name. A concept is an informal classification describing what a class does. There are many concepts in Maverick, here are a few of them:
- Container
- A container of objects such as points or cells.
- 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, procedural, and so on.
- 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 the Maverick toolkit be named in the same manner ascribed to other Maverick classes. The directories that the modules are constructed in should end in \texttt{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 Qt-Based Classes
Qt-based classes are named a bit differently than classes in the mav:: namespace These classes are named according to the following rules:
- Qm(Module Name)ModuleGUI
- Classes that wrap Maverick/Slicer3 modules for use in the designer.
- Qm(Module Name)ModuleGUIPlugin
- Designer plugin definitions for the QmModuleGUI classes.
- QmVTK*
- Classes that are Qt widgets and contain VTK Renders, Render Windows, etc. These classes shouldn't be used directly unless they end in Widget
- Qm*Widget
- Qt classes written for Maverick. Many of these use widgets defined as QmVTK*Display. These are the only GUI classes that should be used directly.
- Qm*WidgetPlugin
- Designer plugin definitions for the Qm*Widget classes.
Naming Files
Files should have the same name as the class, with the 3 letter namespace of the library prepended (e.g., ``mav). Header files are named .h, while implementation files are named either .cxx or .txx, 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 mav 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() and CalculateBoundingBox (CalcBoundingBox() is not allowed because it is not spelled out), the choice is ComputeBoundingBox() 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 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 Methods and Functions in Qt-Based Classes
Qt-Based classes within Maverick present a particular challenge when naming methods. The above rules apply, but it is very imporant to recognize the few caveats:
- overriding methods
- When overriding methods from a superclass in the Qt library, you are generally forced to name your method accordingly. That's an acceptable variation from the rules. If you stay consistent by otherwise naming methods in uppercase, this can be an invaluable clue to other Maverick developers regarding your intentions.
- Maverick-specific signals
- Signals defined in Maverick must be of the form Send*.
- Maverick-specific slots
- Slots defined in Maverick must be of the form Receive*.
Naming variables in Qt GUIs
- When naming instances of Qt widgets, use the following construct
ui<ParentName><UniqueName><WidgetType>
- Examples
uiSmootherRadiusSlider uiROIVolumeTextBox uiROIComputeWidthButton
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 Maverick has several important {\em 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
Don't 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 errors, and unused parameters will generate warnings!
void SetSigma( double sigma, double itkNotUsed(extent) ) { ...some code that doesn't use the parameter extent... }
Shadowed parameters are particularly troublesome in that they often indicate code errors and ambiguities. Again, warnings are 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( int i=0; i<100; i++ ) { ...loop stuff... }
Following our naming conventions also helps avoid shadowed vars. In particular, make sure all member variables begin with m_, functions begin with upper-case 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 args, and the derived class has an instance that takes only one double as an arg, then a warning (i.e., an error) is generated. You must also define an instance in the derived class that takes two args.
Newline at EOF
Some compilers will complain (generate a warning/error) if a file does not end with a newline. 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 mav:: 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() const throws std::exception
Code Layout and Indentation
The following are the accepted Maverick 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.
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.
The declaration of variables within classes, methods, and functions should be one declaration per line.
int i; int j; char * stringname;
Spaces (USE THEM!)
Maverick, 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 paran 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;
KISS
Keep it simple, stupid!
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
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:
#ifndef __mavImage_h #define __mavImage_h #include "itkImageBase.h" #include "itkPixelTraits.h" #include "itkDefaultImageTraits.h" #include "itkDefaultDataAccessor.h" namespace mav { template < class TPixel, unsigned int VImageDimension=2, class TImageTraits=DefaultImageTraits< TPixel, VImageDimension > > class Maverick_EXPORT Image : public itk::ImageBase< VImageDimension > { public: ....stuff... protected: ....stuff... private: ....stuff... }; }//end of namespace #endif //end include guard
Method Definition
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 * Image< TPixel, VImageDimension, TImageTraits > ::GetSpacing( ) 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:
int i; for( 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, powerful 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. (See more at http://www.stack.nl/~dimitri/doxygen/index.html.)
Documenting a Class
Classes should be documented using the \texttt{\\class} and \texttt{\\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 */.
Documentation
The Maverick has adopted the following guidelines for producing supplemental documentation.
- Most documentation should begin as a wiki page
- Documentation for releases should be available as PDF
- Administrative documentation for releases should be available as Word
- Presentations are acceptable in Microsoft PowerPoint format.
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
- Dashboards shall be maintained for all platforms
- Warnings will not be tolerated
- Failing tests are preferred over disabling tests or using case/system-specific hacks for solutions
- 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 Maverick
Code Coverage
Code coverage is everyone's responsibility...