Book Image

Learning C++ Functional Programming

By : Wisnu Anggoro
5 (1)
Book Image

Learning C++ Functional Programming

5 (1)
By: Wisnu Anggoro

Overview of this book

Functional programming allows developers to divide programs into smaller, reusable components that ease the creation, testing, and maintenance of software as a whole. Combined with the power of C++, you can develop robust and scalable applications that fulfill modern day software requirements. This book will help you discover all the C++ 17 features that can be applied to build software in a functional way. The book is divided into three modules—the first introduces the fundamentals of functional programming and how it is supported by modern C++. The second module explains how to efficiently implement C++ features such as pure functions and immutable states to build robust applications. The last module describes how to achieve concurrency and apply design patterns to enhance your application’s performance. Here, you will also learn to optimize code using metaprogramming in a functional way. By the end of the book, you will be familiar with the functional approach of programming and will be able to use these techniques on a daily basis.
Table of Contents (15 chapters)
Title Page
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface

Leveraging the use of C++ language with the C++ Standard Libraries


The C++ Standard Libraries are a powerful set of classes and functions that have many capabilities needed to create an application. They are controlled by the C++ ISO Standard Committee and is influenced by the Standard Template Libraries (STL), which were the generic libraries before C++11 was introduced. All features in Standard Libraries are declared in std namespace and no headers end in .h anymore (except 18 headers of the ISO C90 C Standard Library that is incorporated into the C++ Standard Libraries).

There are several header files containing the declaration of the C++ Standard Libraries. However, it is almost impossible to discuss all header files in these tiny chapters. We will, therefore, talk about some features that we will use most in our daily coding activities.

Placing any objects in the container

Container is an object that is used to store other objects and manage the memory that is used by the objects it contains. An array is a new feature added in C++11 to store the collection of specific data types. It is a sequence container since it stores the same data type objects and arranges them linearly. Let's take a look at the following code snippet:

    /* array.cpp */
    #include <array>
    #include <iostream>

    auto main() -> int
    {
      std::cout << "[array.cpp]" << std::endl;

      // Initializing an array containing five integer elements
      std::array<int, 10> arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

      // Displaying the original elements of the array
      std::cout << "Original Data : ";
      for(auto a : arr) std::cout << a << " ";
      std::cout << std::endl;

      // Modifying the content of
      // the 1st and 3rd element of the array
      arr[1] = 9;
      arr[3] = 7;

      // Displaying the altered array elements
      std::cout << "Manipulated Data: ";
      for(auto a : arr) std::cout << a << " ";
      std::cout << std::endl;

      return 0;
     }

As we can see in the preceding code, we instance a new array named arr, set its length as 10, and only approve the int element. As we can guess, the output of the code is a line of numbers 0 through 9, which is shown in the original data, and the other line will show the altered data, as we can see in the following screenshot:

Note

There is no performance issue if we declare an array using std::array; we use in the array.cpp code and compare it with a usual array as we use in the begin_end.cpp code. However, in modern C++, we are given a new array declaration that has a friendly value semantic, so that it can be passed to or returned from functions by value. Also, the interface of this new array declaration makes it more convenient to find the size, and use it with Standard Template Library (STL)-style iterator-based algorithms.

It is good to use an array as the container since we can store the data and manipulate them. We can also sort and find a specific element if we want. However, since the array is a compile-time non-resizable object, we have to decide the size of the array we intend to use at the very beginning as we cannot change the size later. In other words, we cannot insert or remove the element from the existing array. As a solution to this problem, and for the best practice of using the container as well, we can now use a vector to store our collection. Let's take a look at the following code:

    /* vector.cpp */
    #include <vector>
    #include <iostream>

    auto main() -> int
    {
      std::cout << "[vector.cpp]" << std::endl;

      // Initializing a vector containing three integer elements
      std::vector<int> vect = { 0, 1, 2 };

      // Displaying the original elements of the vector
      std::cout << "Original Data : ";
      for (auto v : vect) std::cout << v << " ";
      std::cout << std::endl;

      // Adding two new data
      vect.push_back(3);
      vect.push_back(4);

      // Displaying the elements of the new vector
      // and reverse the order
      std::cout << "New Data Added : ";
      for (auto v : vect) std::cout << v << " ";
      std::cout << std::endl;

      // Modifying the content of
      // the 2nd and 4th element of the vector
      vect.at(2) = 5;
      vect.at(4) = 6;

      // Displaying the altered array elements
      std::cout << "Manipulate Data: ";
      for (auto v : vect) std::cout << v << " ";
      std::cout << std::endl;

      return 0;
    }

Now, we have a vector instance in our preceding code instead of an array instance. As we can see, we give an additional value for the vector instance using the push_back() method. We can add the value anytime we want. The manipulation of each element is also easier since vector has an at() method that returns a reference to the element of the specific index. The following screenshot is what we will see as the output when running the code:

Note

It is better to always use the at() method instead of the [] operator when we want to access the specific element by its index in a vector instance. It's because, when we accidentally access the out of range position, the at() method will throw an out_of_range exception. Otherwise, the [] operator will give undefined behavior.

Using algorithms

We can sort the elements of the collection we have in array or vector, as well as find specific content of the element. For these purposes, we have to use the algorithm feature provided by the C++ Standard Library. Let's take a look at the following code to demonstrate the sorting element capability in the algorithm feature:

    /* sort.cpp */
    #include <vector>
    #include <algorithm>
    #include <iostream>

    bool comparer(int a, int b)
    {
      return (a > b);
    }

    auto main() -> int
    {
      std::cout << "[sort.cpp]" << std::endl;

      // Initializing a vector containing several integer elements
      std::vector<int> vect = { 20, 43, 11, 78, 5, 96 };

      // Displaying the original elements of the vector
      std::cout << "Original Data : ";
      for (auto v : vect)
      std::cout << v << " ";
      std::cout << std::endl;

      // Sorting the vector element ascending
      std::sort(std::begin(vect), std::end(vect));

      // Displaying the ascending sorted elements
      // of the vector
      std::cout << "Ascending Sorted : ";
      for (auto v : vect)
      std::cout << v << " ";
      std::cout << std::endl;

      // Sorting the vector element descending
      // using comparer
      std::sort(std::begin(vect), std::end(vect), comparer);

      // Displaying the descending sorted elements
      // of the vector
      std::cout << "Descending Sorted: ";
      for (auto v : vect)
      std::cout << v << " ";
      std::cout << std::endl;

      return 0;
   }

As we see in the preceding code, we invoked the sort() method twice. First, we just supplied the range of the elements we wanted to sort. Then we added the comparison function, comparer(), to be provided to the sort() method to gain more flexibility the method has. The output we will see on the console from the preceding code is as follows:

From the preceding screenshot, we can see that we have six elements in a vector at the beginning. We then sort the elements of the vector using a simple sort() method. Then, we invoke the sort() method again, but instead of a simple sort() method, we now supply comparer() to the sort() method. As a result, the vector elements will be sorted descendingly since the comparer() function looks for the greater value from two inputs.

Now, let's move to another capability the algorithm feature has, which is finding a particular element. Let's suppose we have the Vehicle class in our code. It has two private fields named m_vehicleType and m_totalOfWheel, and we can retrieve the value from the getter methods named GetType() and GetNumOfWheel() respectively. It also has two constructors, which are the default constructor and the user-defined one. The declaration of the class should be as follows:

    /* vehicle.h */
    #ifndef __VEHICLE_H__
    #define __VEHICLE_H__

    #include <string>

    class Vehicle
    {
      private:
        std::string vehicleType;
        int totalOfWheel;

      public:
        Vehicle(
          const std::string &type,
          int _wheel);
        Vehicle();
        ~Vehicle();
        std::string GetType() const {return vehicleType;}
        int GetNumOfWheel() const {return totalOfWheel;}
    };

    #endif // End of __VEHICLE_H__

The implementation of the Vehicle class is as follows:

    /* vehicle.cpp */
    #include "vehicle.h"

    using namespace std;

    // Constructor with default value for
    // m_vehicleType and m_totalOfWheel
    Vehicle::Vehicle() : m_totalOfWheel(0)
    {
    }

    // Constructor with user-defined value for
    // m_vehicleType and m_totalOfWheel
    Vehicle::Vehicle( const string &type, int wheel) :
     m_vehicleType(type),
     m_totalOfWheel(wheel)
    {
    }

    // Destructor
    Vehicle::~Vehicle()
    {
    }

We will store a collection of Vehicle in the vector container, and then we will search for some elements based on its property. The code will be as follows:

    /* find.cpp */
    #include <vector>
    #include <algorithm>
    #include <iostream>
    #include "../vehicle/vehicle.h"

    using namespace std;

    bool TwoWheeled(const Vehicle &vehicle)
    {
      return _vehicle.GetNumOfWheel() == 2 ? 
        true : false;
     }

    auto main() -> int
    {
      cout << "[find.cpp]" << endl;

      // Initializing several Vehicle instances
      Vehicle car("car", 4);
      Vehicle motorcycle("motorcycle", 2);
      Vehicle bicycle("bicycle", 2);
      Vehicle bus("bus", 6);

      // Assigning the preceding Vehicle instances to a vector
      vector<Vehicle> vehicles = { car, motorcycle, bicycle, bus };

      // Displaying the elements of the vector
      cout << "All vehicles:" << endl;;
      for (auto v : vehicles)
        std::cout << v.GetType() << endl;
      cout << endl;

      // Displaying the elements of the vector
      // which are the two-wheeled vehicles
      cout << "Two-wheeled vehicle(s):" << endl;;
      auto tw = find_if(
                      begin(vehicles),
                      end(vehicles),
                      TwoWheeled);
      while (tw != end(vehicles))
      {
        cout << tw->GetType() << endl ;
        tw = find_if(++tw, end(vehicles), TwoWheeled);
      }
      cout << endl;

      // Displaying the elements of the vector
      // which are not the two-wheeled vehicles
      cout << "Not the two-wheeled vehicle(s):" << endl;;
      auto ntw = find_if_not(begin(vehicles),
                           end(vehicles),
                           TwoWheeled);
      while (ntw != end(vehicles))
      {
        cout << ntw->GetType() << endl ;
        ntw = find_if_not(++ntw, end(vehicles), TwoWheeled);
      }

      return 0;
     }

As we can see, we instance four Vehicle objects, then store them in vector. There, we try to find the vehicle that has two wheels. The find_if() function is used for this purpose. We also have the TwoWheeled() method to provide the comparison value. Since we are finding the two-wheeled vehicle, we will inspect the totalOfWheel variable in the Vehicle class by invoking the GetNumOfWheel() method. In contrast, if we want to find the element that doesn't conform to the comparison value, we can use the find_if_not() function, which had been added in C++11. The output we get should look like this:

Note

As we can see in the vehicle.cpp code and find.cpp code, we now add the using namespace std; line in the *.cpp files. We do this to make our coding activity become more productive since we don't have to type many words. In contrast, in vehicle.h, we still using std:: followed by the methods or properties name rather than use the std namespace at the beginning. It's best practice to not declare using namespace in header files since the header files are the files we will deliver if we create some libraries for instances. The user of our library may have another method with the same name as the function our library has. It will definitely create conflict between these two functions.

Another algorithm feature we will use most is the for_each loop. Instead of using the for loop, the use of the for_each loop will make our code more concise in many cases. It's also simpler and less error prone than a for loop because we can define a specific function for the for_each loop. Now let's refactor our previous code to use the for_each loop. The code is written as follows:

    /* for_each.cpp */
    #include <vector>
    #include <algorithm>
    #include <iostream>
    #include "vehicle.h"

    using namespace std;

    void PrintOut(const Vehicle &vehicle)
    {
      cout << vehicle.GetType() << endl;
    }

    auto main() -> int
   {
      cout << "[for_each.cpp]" << endl;

      // Initializing several Vehicle instances
      Vehicle car("car", 4);
      Vehicle motorcycle("motorcycle", 2);
      Vehicle bicycle("bicycle", 2);
      Vehicle bus("bus", 6);

      // Assigning the preceding Vehicle instances to a vector
      vector<Vehicle> vehicles = { car, motorcycle, bicycle, bus };

      // Displaying the elements of the vector
      cout << "All vehicles:" << endl;
      for_each(begin(vehicles), end(vehicles), PrintOut);

      return 0;
    }

Now, with the for_each loop, we have a clearer code. We only need to provide the first and last iterator and then pass a function--the PrintOut() function in this case--that will be invoked in each element in the range.