Create own STL algorithms wrapper

Within the previous articles about STL algorithms I introduced the main algorithms sorted by their use case. This analysis of the actual STL algorithms has shown the power of the algorithms but also their issues. The main issues are misleading names and a sometimes-cumbersome notation.

Of course, it is easy to write source code by using the algorithms, but it is sometimes hard to read and understand existing code. Notation matters more than we usually like to believe. Therefore, it may be useful to create an own algorithm wrapper and hide the misleading names and the cumbersome notation behind an easy to use interface.

Within this article I want to show some examples for algorithms wrapper. These wrapper functions add a self-explaining interface and therefore you don’t have to write comments to explain the algorithms. If you use these algorithms without a wrapper, you must write comments to each algorithm to explain its behavior.

 

Example: remove elements from container

The algorithm “remove” is used to remove elements from a container. But unfortunately, this algorithm will not remove the elements. Instead it will shift the elements to delete to the end of the container and returns an iterator to the new logical end. So, you always must explicitly erase the elements after calling the algorithm. I think that’s a perfect candidate for a wrapper function.

template
void RemoveElement(C& container, E element) 
{ 
  // remove (shifts elements but dont deletes them)
  auto newEndIterator = std::remove(container.begin(), container.end(), element);
  
  // delete elements
  container.erase(newEndIterator, container.end());
}

void Print(const std::vector& data)
{
  std::for_each(data.begin(), data.end(), [](int element){ std::cout << element << " "; });
  std::cout << std::endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
  std::vector data{ 5, 7, 9, 2, 4, 9, 3 };

  RemoveElement(data, 9);

  Print(data);  // output is '5 7 2 4 3'

  return 0;
}

 

Example: count equal pairs in two containers

With the algorithm “inner_product” you can – like the name reveals – calculate an inner product. But you can specify own operations and then the function will offer a common map/reduce functionality according to the given algorithm. So, if you set an algorithm the name “inner_product” is wrong as the function no longer calculates the inner product.

The following example shows how to count the equal pairs of two containers. If you put this function into a self-explaining wrapper function, the code will become much easier to understand.

template
int CountEqualPairs(C& container1, C& container2)
{
  return std::inner_product(
    container1.begin(), container1.end(), container2.begin(),
      0, std::plus(), std::equal_to());    
}

int _tmain(int argc, _TCHAR* argv[])
{
  std::vector data1{ 5, 7, 9, 2, 4, 9, 3 };
  std::vector data2{ 1, 0, 9, 8, 4, 3, 3 };
  
  int result = CountEqualPairs(data1, data2);

  std::cout << result << std::endl;   // result is '3'

  return 0;
}

 

Example: pairwise multiplication

The algorithm “transform” is another good candidate for a wrapper. This algorithm can be used for a wide range of use cases and therefore it has a cumbersome notation. So, if you read an implementation using this algorithm you must stop for a short (or sometimes long) moment and think about the purpose of the source code. In such cases you are happy if the developer has written a comment and explains the behavior of the algorithm. But often such a comment is missing.

As explained above, a self-explaining wrapper can help to write clean code which is easy to understand and to maintain. The following example shows a possible implementation for a pairwise multiplication.

template
C PairwiseMultiply(C& container1, C& container2)
{
  C target;

  std::transform(
    container1.begin(), container1.end(),
    container2.begin(),
    std::back_inserter(target),
    std::multiplies());  

  return target;
}

void Print(const std::vector& data)
{
  std::for_each(data.begin(), data.end(), [](int element){ std::cout << element << " "; });
  std::cout << std::endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
  std::vector data1{ 5, 7, 9, 2, 4, 9, 3 };
  std::vector data2{ 1, 0, 9, 8, 4, 3, 3 };
  std::vector target;
  
  target = PairwiseMultiply(data1, data2);

  Print(target);  // output is '5 0 81 16 16 27 9'

  return 0;
}

 

Summary

The STL algorithms are sometimes hard to understand. If you read source code and you find an algorithm, you have to stop for a moment and think about its purpose. A wrapper library helps to hide these implementation details behind a self-explaining interface. This will allow you to create more readable and maintainable source code.

Advertisements
Dieser Beitrag wurde unter C++ veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

w

Verbinde mit %s