Jordi Inglada
16 Jan 2014

OTB++11

Modern C++

I have just recently had a look at the last C++ ISO standard (known as C++11). I was not aware of all the fancy features that have been included in the language.

Some of the most interesting features are:

  • auto,
  • range-based for loops,
  • general constant expressions,
  • uniform initialisation,
  • type aliases,
  • decltype,
  • variadic templates,
  • type safe threading,
  • user-defined literals,
  • move semantics

There are also new containers and algorithms in the STL.

During the end of the year break, I spent some time reading and watching on line presentations about the new C++. I recommend watching Bjarne Stroustrup's keynote at Going Native 2012 (link) and Scott Meyer's talk at Going Native 2013 where he even anticipates some C++14 features (link).

Stroustrup introduces his talk by saying:

I use C++11 freely. Examples include auto, general constant expressions, uniform initialisation, type aliases, type safe threading, and user-defined literals. C++11 features are only just starting to appear in production compilers, so some of my suggestions are conjecture. Developing a "modern style," however, is essential if we don't want to maintain newly-written 1970s and 1980s style code in 2020.

After listening at him, I realised that the most easy to use features alone could make C++ programming much more fun and simple. Since I have spent some time in the last 2 years flirting with functional and dynamic typed (or at least with type inference) languages (Scheme, Clojure, Python, SML), I wanted to see how using C++11 affects the style of OTB code.

As usual, GCC has been the first compiler to make available these new features (closely followed by Clang). C++11 is available in recent versions of GCC using -std=c++11. So I recompiled OTB with this flag and started modifying some of the Software Guide examples using C++11 features.

All in all, I can say that these features make OTB code shorter, clearer and maybe safer. I report some examples here.

Examples

auto

The auto keyword allows to declare variables without explicitly stating their type and puts this burden on the compiler. This is not dynamic typing, this is type inference and nothing changes in terms of what the compiler generates. So you can write:

auto x = sqrt(2.0);

and the type of x will be deduced from the return value type of sqrt. You can also do this:

auto x=0;
auto y=0.0;

and x will be of type int and y will be of type double. If you want to force other types, you have to use the classical declarations.

How to benefit from auto when using OTB? A common pattern in OTB code is to instantiate a smart pointer using the New() class method. Sometimes, we just want to create one object of a given type and we do this:

typedef otb::ImageFileReader<ImageType> ReaderType;
ReaderType::Pointer reader = ReaderType::New();

typedef otb::ImageFileWriter<ImageType> WriterType;
WriterType::Pointer writer = WriterType::New();

in order to create a reader and a writer. That is, since type names are long we define a type using typedef and then we use it twice, once for Pointer and once for New.

Using auto, we can reduce the boilerplate like this:

auto reader = otb::ImageFileReader<ImageType>::New();
auto writer = otb::ImageFileWriter<ImageType>::New();

The same applies for filters, for which usually we only use one instance:

typedef itk::RescaleIntensityImageFilter<ImageType,
                                         OutputImageType> RescalerType;
RescalerType::Pointer rescaler = RescalerType::New();

becomes

auto rescaler = itk::RescaleIntensityImageFilter<ImageType,
                                                 OutputImageType>::New();

which is once again much shorter.

Another example in a for loop:

for (PolygonListType::Iterator it = polygonList->Begin();
     it != polygonList->End(); ++it)
  {
  DataNodeType::Pointer newPolygon = DataNodeType::New();
  newPolygon->SetPolygonExteriorRing(it.Get());
  tree->Add(newPolygon, multiPolygon);
  }

becomes

for (auto it = polygonList->Begin();
     it != polygonList->End(); ++it)
  {
  auto newPolygon = DataNodeType::New();
  newPolygon->SetPolygonExteriorRing(it.Get());
  tree->Add(newPolygon, multiPolygon);
  }

using

The using keyword allows to define template aliases. Whenever we write this:

typedef otb::Image<unsigned int, 2> ImageType;
typedef otb::VectorImage<unsigned int, 2> VectorImageType;
typedef otb::Image<double, 2> DoubleImageType;
typedef otb::VectorImage<double, 2> DoubleVectorImageType;

we could do this:

using ImageType = otb::Image<unsigned int, 2>;
using VectorImageType = otb::VectorImage<unsigned int, 2>;
using DoubleImageType = otb::Image<double, 2>;
using DoubleVectorImageType = otb::VectorImage<double, 2>;

For people like me who have always found counterintuitive the order of the types in a typedef, this is much clear. I know that the code is not shorter, but for a C++ beginner, this should be much clearer.

One cool thing that using allows and that typedef doesn't is partial template specialisation1:

template<typename T> 
using Image2D = otb::Image<T, 2>;
auto im = Image2D<int>::New();  

decltype

Sometimes, we don't want to mentally track the type names, but we know that to objects should have the same type. decltype allows to declare the type of one object by using the name of another object:

ImageType::IndexType start = {0, 0};
decltype(start) start2 = {0, 0};

In OTB, at first, I didn't see a way to use decltype on smart pointers and templates, since we usually manipulate ObjectType::Pointer, but the template parameters are simply ObjectType. Fortunately, ITK designers declared an ObjectType in the SmartPointer class which is an alias for the type of the pointed object.

I found an example where this is useful (the orthofusion example in the Tutorial chapter of the Software Guide).

The original code defines a type for a projection and creates an object:

typedef otb::GenericMapProjection<otb::TransformDirection::INVERSE>
        InverseProjectionType;

InverseProjectionType::Pointer utmMapProjection =
                               InverseProjectionType::New();

Later in the program, we can use the defined type as a template parameter for a filter:

typedef otb::OrthoRectificationFilter<ImageType, DoubleImageType,
                                      InverseProjectionType> 
             OrthoRectifFilterType;
OrthoRectifFilterType::Pointer orthoRectifPAN =
                               OrthoRectifFilterType::New();

If we created the projection without defining the type like this:

auto utmMapProjection =
     otb::GenericMapProjection<otb::TransformDirection::INVERSE>::New();

how can we then instantiate the filter with a type which has not been defined? Like this:

auto orthoRectifPAN =
      otb::OrthoRectificationFilter<ImageType,
                                    DoubleImageType,
                                    decltype(utmMapProjection)::ObjectType>::New();

I am not sure whether this is a good solution in production code which has to be maintained long time after it was written, but it is for sure an interesting solution when prototyping processing chains.

Lambdas

Lambdas, or anonymous functions, are one of the most exciting things in C++11 when one has used them in LISP-like languages or even in Python. They make fast prototyping much easier mainly when making a heavy use of the STL algorithms because they can replace functors or plain functions.

Usually, STL algorithms can be specialised by passing them functions as parameters. If one wants to store state, a functor or function object (a class which defines the () operator) is used.

The inconvenient of functions and functors is that they are defined away from where they are used, so they are not ideal when they are only used once. Functors are worse than functions since they need much boilerplate for only one useful method.

Lambdas are the ideal solution for this, since they are defined locally. They can store state by capturing local variables either by reference or by value.

OTB iterators are not fully compatible with the STL, so one can think that lambdas would not be very useful with OTB specific code. But actually, there are many filters which can be implemented by specialising one of the N-ary functor filters. These filters are useful when one wants to implement a filtering operation which can't be obtained with the otb::BandMathFilter, as for instance when a local neighbourhood is needed.

One example of this is the change detection framework. A long time ago when I wrote this example I had to use a lot of code to achieve a simple thing like computing the mean difference around a pixel.

First of all, I needed a functor to implement the processing in the neighbourhood:

template<class TInput1, class TInput2, class TOutput>
class MyChangeDetector
{
public:
  MyChangeDetector() {}
  ~MyChangeDetector() {}
  inline TOutput operator ()(const TInput1& itA,
                             const TInput2& itB)
  {
    TOutput result = 0.0;

    for (unsigned long pos = 0; pos < itA.Size(); ++pos)
      {

      result += static_cast<TOutput>(itA.GetPixel(pos) - itB.GetPixel(pos));

      }
    return static_cast<TOutput>(result / itA.Size());
  }
};

That is, a class definition with empty constructor and destructor. Then I needed to inherit from the binary functor which performs a neighbourhood filtering and specialise it using my functor.

template <class TInputImage1, class TInputImage2, class TOutputImage>
class ITK_EXPORT MyChangeDetectorImageFilter :
  public otb::BinaryFunctorNeighborhoodImageFilter<
      TInputImage1, TInputImage2, TOutputImage,
      MyChangeDetector<
          typename itk::ConstNeighborhoodIterator<TInputImage1>,
          typename itk::ConstNeighborhoodIterator<TInputImage2>,
          typename TOutputImage::PixelType> >
{
public:
  typedef MyChangeDetectorImageFilter Self;

  typedef typename otb::BinaryFunctorNeighborhoodImageFilter<
      TInputImage1, TInputImage2, TOutputImage,
      MyChangeDetector<
          typename itk::ConstNeighborhoodIterator<TInputImage1>,
          typename itk::ConstNeighborhoodIterator<TInputImage2>,
          typename TOutputImage::PixelType>
      >  Superclass;

  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  itkNewMacro(Self);

protected:
  MyChangeDetectorImageFilter() {}
  virtual ~MyChangeDetectorImageFilter() {}

private:
  MyChangeDetectorImageFilter(const Self &);
  void operator =(const Self&);

};

So again, a class definition for nothing. I could maybe just have defined a type, but the code using it would not have been as simple as this:

typedef MyChangeDetectorImageFilter<InputImageType1, InputImageType2,
    ChangeImageType>      FilterType;
FilterType::Pointer   filter = FilterType::New();

Even if I had defined a type alias for the filter in order to avoid to inherit from the existing filter, the functor definition is an overkill and it is located far from where it is used.

Using lambdas, the code becomes this:

using CPT = ChangeImageType::PixelType;
using CNIType1 = itk::ConstNeighborhoodIterator<InputImageType1>;
using CNIType2 = itk::ConstNeighborhoodIterator<InputImageType2>;

That is, 3 lines of type aliases for a simpler notation. Then the lambda:

std::function<CPT (CNIType1, CNIType2)>
  cdt = [](CNIType1 itA, CNIType2 itB){
  CPT result{0};
  for (auto pos = 0; pos < itA.Size(); ++pos)
    result += static_cast<CPT>(itA.GetPixel(pos) - itB.GetPixel(pos));
  return static_cast<CPT>(result / itA.Size());
};                                            

And finally the filter instantiation:

auto filter = otb::BinaryFunctorNeighborhoodImageFilter<InputImageType1,
                                                        InputImageType2,
                                                        ChangeImageType,
                                                        decltype(cdt)>::New();

If we had wanted to have a function with state, objects in the scope where the lambda is defined can be captured by value or by reference within the [], so we are very close to what a functor may achieve with much less code.

The main advantage here is that all the code is in the same location thanks to the possibility of creating a function anywhere in the sources. If I understand it well, lambdas are inlined, which means that the code is faster than a function call.

Conclusion

I have only started to use this kind of code, but I think that once you start using auto and lambdas it must be very difficult to go back to C++98/03.

The only drawback with using C++11 is dropping compatibility with old compilers, but this is not an issue for most of the things I do. So I can start writing modern OTB code instead of poo (plain old OTB)!

Footnotes:

1

With GCC 4.7 this only seems to work out of a scoped block.

Tags: cpp otb
Creative Commons License
jordiinglada.net by Jordi Inglada is licensed under a Creative Commons Attribution-ShareAlike 4.0 Unported License. RSS. Feedback: info /at/ jordiinglada.net Mastodon