C++ Tidbits – Generic Programming

What is generic programming? In a nutshell is a mechanism that allows the programmer to delay the data type used in a class / method to run time. If my vague description is not enough you could check here.

The main practical reason for generic programming is to avoid duplication. For example one may have a method or class that would need to be duplicated when using different data types. That grows the code base and may be prone to mistakes after copying and editing.

I am writing this code using C++ on Visual Studio 2017.

Let’s start by declaring an array of integers, populating it with some values and then calling a function to display the contents of the array:

	const int	SIZE = 10;

	// **** instantiate and populate an integer array ****
	int numbers[SIZE];
	for (int i = 0; i < SIZE; i++)
		numbers[i] = i;

	// **** display the integer array ****
	displayInt(numbers, SIZE);
	cout << endl;

We can now write additional code to create a string array, populating the array with some names and finally displaying the array in a similar fashion that we did with the integers.

	// **** instantiate and populate a string array ****
	string names[SIZE] = {"James", "Jim", "John", "Jane", "Jill", 
						  "Mary", "Gab", "Nat", "Mike", "Al"};

	// **** display the string array ****
	displayStr(names, SIZE);
	cout << endl;

What follows is the output for this section in the program:

arr: 0 1 2 3 4 5 6 7 8 9

arr: James Jim John Jane Jill Mary Gab Nat Mike Al

The code used for the support / convenience functions follows:

void displayInt(int arr[], int size) {
	cout << "arr: ";
	for (int i = 0; i < size; i++)
		cout << arr[i] << " ";
	cout << endl;
}


void displayStr(string arr[], int size) {
	cout << "arr: ";
	for (int i = 0; i < size; i++)
		cout << arr[i] << " ";
	cout << endl;
}

Not good. We can see that we wrote very similar code, once to handle an integer array and the second to handle a string array. Perhaps there is a better way to get this task done. Before we attempt a solution, let’s compare a set of values as illustrated by the following example:

// **** comparisons using the same function ****
	int a = 20;
	int b = 10;
	cout << "main <<<   a: " << a << endl;
	cout << "main <<<   b: " << b << endl;
	cout << "main <<< max: " << max(a, b) << endl;
	cout << endl;

	string aa = "cake";
	string bb = "aardvark";
	cout << "main <<<  aa: " << aa << endl;
	cout << "main <<<  bb: " << bb << endl;
	cout << "main <<< max: " << max(aa, bb) << endl;
	cout << endl;

	double aaa = 1.23;
	double bbb = 4.56;
	cout << "main <<< aaa: " << aaa << endl;
	cout << "main <<< bbb: " << bbb << endl;
	cout << "main <<< max: " << max(aaa, bbb) << endl;
	cout << endl;

The max() function in the first case takes a couple integers. The same function then was used to compare two strings. Finally the same function was used to compare two doubles.

Let’s take a look at the output of the test program:

main <<<   a: 20
main <<<   b: 10
main <<< max: 20

main <<<  aa: cake
main <<<  bb: aardvark
main <<< max: cake

main <<< aaa: 1.23
main <<< bbb: 4.56
main <<< max: 4.56

Let’s take a look at the implementation of the max() function:

template <typename T>
T max(T &a, T &b) {
	if (a >= b)
		return a;
	else
		return b;
}

Let’s now try a second example of writing a Stack class that would hold different types. A stack is defined as a FILO (first in last out) data structure. It operates the same as a mechanical stack found in most cafeterias holding dishes. In an empty stack, we start pushing dishes, one by one. The first dish pushed will be the last popped out.

Let’s write some code to push handle integers using an integer stack followed by similar code to handle strings on a stack of strings.

	// **** ****
	Stack<int>		intStack;

	// **** push values ****
	cout << "main <<< push: " << intStack.push(10) << endl;
	cout << "main <<< push: " << intStack.push(20) << endl;
	cout << "main <<< push: " << intStack.push(30) << endl;
	cout << "main <<< push: " << intStack.push(40) << endl;
	cout << "main <<< push: " << intStack.push(50) << endl;
	cout << endl;

	// **** ****
	cout << "main <<< intStack.peek(): " << intStack.peek() << endl;
	cout << endl;

	// **** pop values ****
	cout << "main <<< intStack.pop: " << intStack.pop() << endl;
	cout << "main <<< intStack.pop: " << intStack.pop() << endl;
	cout << "main <<< intStack.pop: " << intStack.pop() << endl;
	cout << "main <<< intStack.pop: " << intStack.pop() << endl;
	cout << "main <<< intStack.pop: " << intStack.pop() << endl;
	cout << endl;

	// **** ****
	Stack<string>	stringStack;

	// **** push values ****
	cout << "main <<< push: " << stringStack.push("John") << endl;
	cout << "main <<< push: " << stringStack.push("Peter") << endl;
	cout << "main <<< push: " << stringStack.push("Paul") << endl;
	cout << "main <<< push: " << stringStack.push("Mary") << endl;
	cout << "main <<< push: " << stringStack.push("Kate") << endl;
	cout << endl;

	// **** ****
	cout << "main <<< stringStack.peek(): " << stringStack.peek() << endl;
	cout << endl;

	// **** pop values ****
	cout << "main <<< stringStack.pop: " << stringStack.pop() << endl;
	cout << "main <<< stringStack.pop: " << stringStack.pop() << endl;
	cout << "main <<< stringStack.pop: " << stringStack.pop() << endl;
	cout << "main <<< stringStack.pop: " << stringStack.pop() << endl;
	cout << "main <<< stringStack.pop: " << stringStack.pop() << endl;
	cout << endl;

The output for the program follows:

main <<< push: 10
main <<< push: 20
main <<< push: 30
main <<< push: 40
push <<< stack is FULL!!!
main <<< push: 50

main <<< intStack.peek(): 40

main <<< intStack.pop: 40
main <<< intStack.pop: 30
main <<< intStack.pop: 20
main <<< intStack.pop: 10
pop <<< stack is EMPTY!!!
main <<< intStack.pop: 10

main <<< push: John
main <<< push: Peter
main <<< push: Paul
main <<< push: Mary
push <<< stack is FULL!!!
main <<< push: Kate

main <<< stringStack.peek(): Mary

main <<< stringStack.pop: Mary
main <<< stringStack.pop: Paul
main <<< stringStack.pop: Peter
main <<< stringStack.pop: John
pop <<< stack is EMPTY!!!
main <<< stringStack.pop: John

We need to declare the stack in a header file so it can be used in different programs. The Stack.h header file follows:

#pragma once

#include "pch.h"
#include <iostream>
#include <string>
#include "Stack.h"

using namespace std;


template <class T>
class Stack
{
private:
	static const int SIZE = 4;

private:
	T dataStore[SIZE];
	int top;

public:

	// **** constructor ****
	Stack();

	// **** destructor ****
	~Stack();

	// **** other methods ****
	T push(T val);
	T pop();
	T peek();
};

We now define the Stack.cpp implementation as follows:


#include "pch.h"
#include <iostream>
#include <string>
#include <stdexcept>

#include "Stack.h"


using namespace std;


// **** constructor ****
template <class T>
Stack<T>::Stack()
{
	top = -1;
}

// **** destructor ****
template <class T>
Stack<T>::~Stack()
{
}

// **** ****
template <class T>
T Stack<T>::push(T val)
{

	// **** check if the stack is full ****
	if (top >= SIZE - 1) {
		cout << "push <<< stack is FULL!!!" << endl;
	}
	else {
		// **** push the value on the top of the stack ****
		top++;
		dataStore[top] = val;
	}

	// **** ****
	return val;
}

// **** ****
template <class T>
T Stack<T>::pop()
{

	T val;

	// **** check if the stack is empty ****
	if (top < 0) {
		cout << "pop <<< stack is EMPTY!!!" << endl;
		val = dataStore[0];
	}
	else {

		// **** remove and return the top element ****
		val = dataStore[top];
		top--;
	}

	// **** ****
	return val;
}

// **** ****
template <class T>
T Stack<T>::peek()
{

	// **** check if the stack is empty ****
	if (top < 0) {
		cout << "peek <<< stack is EMPTY!!!" << endl;
		return (T)NULL;
	}
	else {

		// **** return the top element ****
		return dataStore[top];
	}
}

We can push a set of integers into the intStack and a set of strings into the stringStack. The implementation seems to work well. We push some items and reach the capacity of the stacks. The capacity could be larger of it could grow and shrink dynamically. We will not cover such operations in this post. We then pop elements form the stacks. A message is displayed when the stack is empty. The last element in the stack is returned. Note that this does not seem to be a clean implementation. If you attempt to pop a dish from an empty stack at the cafeteria, you will not get the dish that was last popped out :o( In the next post we will cover exceptions. In that case the stack is full or empty we will return an exception.

Another thing that should be done with the stack is to clean up the values as they are popped. For security reasons it is better to clear them after a value is popped. Of course, if we cleared them what would we return if the stack is empty? We could return a negative number or an empty string. What if we would be using doubles? All this can properly be implemented after we add support for exceptions.

Hope you enjoyed the post. When developing classes we need to make sure their operation makes sense. Just writing code and not testing its use if a bad practice. If you use Test Driven Development (TDD) you should able to find issues and be able to address them before the code is promoted for production.

If you have comments or question regarding this or any other post please leave me a message below.

Keep on developing quality software;

John

Please follow me on Twitter:  @john_canessa

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.