Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Learning C++ Functional Programming

You're reading from   Learning C++ Functional Programming Explore functional C++ with concepts like currying, metaprogramming and more

Arrow left icon
Product type Paperback
Published in Aug 2017
Publisher
ISBN-13 9781787281974
Length 304 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Wisnu Anggoro Wisnu Anggoro
Author Profile Icon Wisnu Anggoro
Wisnu Anggoro
Arrow right icon
View More author details
Toc

Table of Contents (9) Chapters Close

Preface 1. Diving into Modern C++ 2. Manipulating Functions in Functional Programming FREE CHAPTER 3. Applying Immutable State to the Function 4. Repeating Method Invocation Using Recursive Algorithm 5. Procrastinating the Execution Process Using Lazy Evaluation 6. Optimizing Code with Metaprogramming 7. Running Parallel Execution Using Concurrency 8. Creating and Debugging Application in Functional Approach

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:

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:

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:

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.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image