VTK

From bone.wiki
(Redirected from Creating VTK Classes)
Jump to navigation Jump to search

Creating VTK Classes

[edit]

Generic C++ Tips

[edit]

Please familiarize yourself with C++ Development. Tips there apply to VTK classes too, which are after all C++ classes.

Minimize includes in headers

[edit]

The general rule of header files in VTK is that you include only the header file of the superclass. For everything else, you merely forward declare it in the header. See C++ Development#Minimize includes in headers for more info on forward declarations.

This is a good rule to follow where possible in C++ anyways, but in VTK it is particularly important, because many kinds of objects (e.g. STL - Standard Template objects) will cause Python Wrapping of the class to fail if used in the header file.

Use vtkSmartPointers

[edit]

VTK requires you to create instances of VTK objects only with the class's static New method, like this:

vtkImageData* image = vtkImageData::New();

The reason for this is that VTK uses reference counting internally, and will automatically delete objects that are no longer used. This is very convenient, because you can create an object somewhere, and link it into one or more pipeline, and not have worry about exactly when to delete it. It has however the disadvantage that when objects are created this way, they will not be automatically destroyed when they go out of scope. This is it should be - we only want them destroyed when their reference count goes to zero. However, you must remember to manually call Delete when the object goes out of scope, like this:

image->Delete();

This may not actually delete the object, it merely decrements the reference counter. It's easy to forget this (or to overlook paths that can take you out of scope - for instance if an exception is thrown.) If you forget, this becomes a memory leak. The best solution to this is to use a vtkSmartPointer, like this:

vtkSmartPointer<vtkImageData> image = vtkSmartPointer<vtkImageData>::New();

This is the only line of code you have to change compared to the abover example - otherwise you can use the variable image exactly as before. Only now, as soon as image goes out of scope, the destructor of the SmartPointer will be called, and it will automatically call the Delete method of image.

NOTE: You don't always need a SmartPointer. If you are getting the pointer to an existing object for some temporary calculation, a bare pointer is adequate.

TIP: You can use regular expressions to search and replace for all creations of VTK objects.

The regular expression search string is:

(vtk\w+) *\* *(\w+) *= *\1::New

And the replace string is:

vtkSmartPointer<$1> $2 = vtkSmartPointer<$1>::New

So for example with sed with would be:

sed -i 's/(vtk\w+) *\* *(\w+) *= *\1::New/vtkSmartPointer<$1> $2 = vtkSmartPointer<$1>::New/' yourfile.cxx

although I actually use TextMate for this.

Note that you amy still have to go through and replace and now-unnecessary delete commands.

Use Doxygen style comments

[edit]

to write

Example: Improving SimilarityRegistration

[edit]

SimilarityTransform is a class that I modified heavily to improve the code. Here are some of the changes:

Class Name

[edit]

Formerly it was called itkSimilarityRegistration . This is confusing, because although it uses ITK classes, it is not itself an ITK class, and does not inherit from an ITK base class, and does not behave like an ITK class. I therefore renamed it to AIMSimilarityRegistration.

Includes moved to .cxx file

[edit]

See C++_Development#Minimize_includes_in_headers

Comments moved to header file

[edit]

The comments at the top of the file about what it does and who wrote it and why, get moved from the .cxx file to the .h file (see C++ Development#Put_comments_in_the_appropriate_place). (By the way, some of these comments used Doxygen style markers. But Doxygen parses only header files.)

Added Doxygen style comments to all methods

[edit]

Added Doxygen style comments to all function calls (at least all the public ones). This makes it much easier for someone to use or modify the class.

Replaced static New with standard constructor

[edit]

The static New method and the Delete method are replaced by standard a constructor and destructor. The static New method pattern is typically employed where the memory management is centralized, and newly created objects need to be registered. ITK and BTK classes have a centralized memory management with reference counting. However AIMSimilarityRegistration does not participate.

VTK objects assigned to vtkSmartPointers

[edit]

New VTK objects are assigned to a VTKSmartPointer. Previously they were assigned to bare pointers and no Delete was ever called on them, so they were (potentially large) memory leaks. Note that ITK pointers that you get from itk class traits are automatically Smart pointers. See Creating_VTK_Classes#Use_vtkSmartPointers .

Moved helper classes to .cxx

[edit]

The helper classes CommandIterationUpdate and RegistrationInterfaceCommand seem only intended to be used within AIMSimilarityRegistration, so they get moved to the .cxx file from the .h file. Remember that anything in the .h file gets exposed in any file where the .h file is included.

String Handling

[edit]

string handling: previously used char arrays, generally very obtuse code with lots of memory leaks and arbitrary string size limits. Replaced with std::string, std::ostringstream and boost::format. (see C++_Development#Prefer_iostreams_to_printf_and_scanf and C++_Development#Prefer_iostreams_to_printf_and_scanf)

Error handling done with C++ exceptions

[edit]

Error handling: done with C++ exceptions. Note that in comparison VTK typically uses return values to indicate error status (ITK uses exceptions). Either approach can work, but C++ exceptions more often do correct thing with less work. (e.g If the caller does not explicitly handle a return value indicating an error, it continues executing as normal, which is bad, whereas if the caller does not explicitly handle an exception, the program terminates, which is good.)

Errors and warnings to cerr

[edit]

Error and warning messages should be to std:cerr, not to std::cout. In most cases this will make no difference, but it gives the user a chance to direct the error messages somewhere different than the regular output. (I often do this for long-running jobs - I want to examine the whole output after it's done, but I want to see errors and warnings immediately.)

Near-identical methods replaced with single templated method

[edit]

TransformImage previously had two versions, one for char data and one for short data. Replaced with a single templated version. Now called like this for example:

TransformImage<short>(outTransform,movingImage,fixedReader,m_movingLog);

It's much easier to maintain a single version of the function than to try to keep two nearly identical versions in sync.

vtkFileParser replaced with boost::property_tree

[edit]

vtkFileParser is gone, since it's a reinvention of a standard wheel. Replaced with boost::property_tree. The user can either pass in a set of configuration parameters (SetConfiguration), or specify a file name from which to read the parameters (ReadConfigFile). There is a method VerifyConfiguration that will check that all required parameters are present and possibly provide default values.

Manually parsing command line parameters replaced with boost::program_options

[edit]

Parsing command line options is a standard wheel that should not be reinvented. It is much more robust and concise to use boost::program_options instead of writing a huge switch/case statement. (This doesn't apply to the class AIMSimilarityRegistration itself, but to the program SimilarityRegistration which uses it.)

Minor changes

[edit]

PrintSampleConfigFile now takes a stream as an argument, so it can be used to send the example to Config file to cout, or to a file, or anywhere.

For file streams, the following is redundant, because leaving the scope where fout is defined will call its destructor and close it automatically:

   if (fout.is_open())
     {
     fout.close();
     return -1;
     }