CS235 Unit 06 Exercise: Templates
Table of Contents
1. Introduction
- About
- Templates are a way we can write functions and classes that work with a "placeholder" data type, instead of hard-coding a data type. These can be useful when we want to create a container that can store any data type, or otherwise implement some kind of function that can operate on any data type.
- Goals
- Practice creating a templated function
- Practice creating a templated storage class
- Setup
- Starter files are available here: https://gitlab.com/moosadee/courses/-/tree/main/wip_exercises/starter_code/c3_u06_Templates?ref_type=heads
2. Project setup
2.1. CS 235 vs. CS 250
- CS 235
- I have not created personal repositories for students in CS 235. If you would like, I can set it up for you, OR you can create your own repository (and you can skip the merge request part), OR you can forego using a repository and turn in your code files on Canvas.
- CS 250
- CS 250 students have a personal repository for the class. Please follow the steps to create a new branch and to create a merge request at the end.
2.2. Creating a new branch
BEFORE getting started on the assignment, make sure to go back to the main branch and pull latest. This way, you will have the most up-to-date version of your repository, and you'll be starting from a shared base-point.
git add . && git commit -m "backup" && git push
to make sure to backup any work on your current branch.git checkout main
to check out the main branch again.git pull
to pull latest changes from the server.
Next, you will create a new branch to get started from:
git checkout -b u06ex
to create a new branch for u05ex.
2.3. Starter code files
Use your IDE to create a new project. Make sure you're putting the project inside your repository directory! Also, the project should have the exercise number in the name, such as "U06 Templates".
Use the starter code located here: https://gitlab.com/moosadee/courses/-/tree/main/wip_exercises/starter_code/c3_u06_Templates?ref_type=heads add the files (except the Makefile) to your project.
2.4. About the project
This project includes several distinct parts, with different ways to show how templates can be used.
3. Observation: Display
Within the Functions.h file you will see the templated function, Display:
template <typename T> void Display( const vector<string>& header, const vector<vector<T>>& data ) { int col_width = 80 / header.size(); cout << left << fixed << setprecision( 2 ); for ( auto& head : header ) { cout << setw( col_width ) << head; } cout << endl << string( 80, '-' ) << endl; for ( auto& row : data ) { for ( auto& col : row ) { cout << setw( col_width ) << col; } cout << endl; } }
This function is already implemented, and its purpose is to write out a table of data.
We don't want to have to make duplicate functions for "a table of integer data",
"a table of float data", "a table of string data", so we use templates to make one
function template that instead deals with a placeholder type T
- "a table of T
data".
The item using the template is the input parameter, const vector<vector<T>>& data
.
It might look a little weird - a vector of vectors of T objects.
Basically, it is an array of rows, and then each row contains an array of columns of data.
You don't really have to worry about the specifics here, since the function is already implemented.
The main idea is that the data could be any data type, so the placeholder T
is being used.
If you run the program, you can see the result of the Display test function:
-------------------- Test_Display (Manual tests) -------------------- Test 1 - Classes Class 1 Class 2 Class 3 Class 4 -------------------------------------------------------------------------------- CS 134 CS 200 CS 235 CS 250 ASL 120 ASL 122 ASL 135 ASL 145 Test 2 - Minimum wage vs. Median monthly rent 2010 2000 1990 1980 1970 1960 1950 -------------------------------------------------------------------------------- 7.25 5.15 3.80 3.10 1.60 1.00 0.75 841.00 602.00 447.00 243.00 108.00 71.00 42.00
4. Templated function: Add
Now we will implement the Add
function, a simple function that adds two items
of any data type together (as long as the +
operation has been implemented for it).
You will also write two unit tests to check your work.
A templated function definition must go in the .h file, not the .cpp file. Technically, a templated function is not C++ code, but a special type of code that the compiler uses to generate new functions… So while we only write one version of "Add", the compiler will see all usages of our function and create an "int version", "float version", etc. based on what is going to be used.
Templated functions take this form:
template <typename T> RETURNTYPE FUNCTIONNAME( PARAMETERS ) { }
4.1. Add function
The Add function will have a T
return type, and take in two T
parameters.
Within the function, add the two parameters together and return the result.
4.2. Add unit test
Declare the tester function here in the .h file. The tester function isn't templated, so the declaration will go here and its definition will go in Functions.cpp.
void Test_Add();
For the function definition, create at least two tests. One should take in integer inputs and one should take in float inputs, and make sure the math checks out for both. For example:
Test | Inputs | Expected output |
---|---|---|
1 | 2.5, 3.25 | 5.75 |
2 | 3, 7 | 10 |
The form I use for my unit tests is as follows:
{ // test begin float input_a = VALUE; float input_b = VALUE; float expected_result = VALUE; float actual_result = FUNCTIONNAME( input_a, input_b ); string test_name = "description"; sring result = ""; if ( actual_result != expected_result ) { result = "[FAIL] "; } else { result = "[PASS] "; } // Use cout to display the test name, result, and the inputs / expected result / actual result. } // test end
4.3. Starting the test in main()
Make sure that you're calling your test from within the main()
function as well.
Build and run the project to make sure everything is running and operating as intended.
5. Templated function in a class: Log
The Log
class in Log.h is not a templated class, but it contains a templated member function:
void Log::Out( string name, T value )
.
The purpose of this Log class would be for logging a program's functionality as it runs, as well as variable values. The Out function takes in a name (such as the variable's name) and then a value, which could be any data type. Then that data is written out to a log file.
The unit test for the Out function is already written. Just implement the Log::Out
function
by writing out the name
and value
to the output file, which is the variable named m_log
.
(You can treat m_log
like you would cout
.)
6. Templated class: SmartFixedArray
Finally, one of the more common uses of templates - a templated structure that stores data. A data structure.
Within SmartFixedArray.h a templated class is already created, and unit tests already exist for the class. You will just be implementing the member functions.
The SmartFixedArray
has the following member variables:
T m_array[100]
- an array of "some data type".const int ARRAY_SIZE
- a named constant that stores the value 100, the size of the array.int m_itemCount
- The amount of items currently stored in the array. Starts at 0, and is added to each time a new value is added.
6.1. void SmartFixedArray<T>::Clear()
The clear function will "lazy-delete" the contents of the array. This means that we set it up so that it looks empty to the outside and when new items are added they overwrite any old data.
Basically, the implementation here is to set the m_itemCount
to 0.
6.2. int SmartFixedArray<T>::Size() const
This function returns how many items are currently stored in the array. So you will be returning the value from m_itemCount
.
6.3. bool SmartFixedArray<T>::IsFull() const
This returns whether the array is full or not. Remember that ARRAY_SIZE
contains the amount of total "slots" in the array,
and m_itemCount
contains how many items are currently stored within. The array is full if these two values equal each other.
6.4. void SmartFixedArray<T>::PushBack( T newItem )
This function will add a new item to the array. New items will need to go at the first
available spot, such as index 0, then 1, then 2, etc. The new data that will be saved
comes in the form of the input parameter, newItem
.
Error check: First off, we need to do an error check: If the array is full, then throw an exception - we cannot add any more items!
Normal functionality: Remember that to set an array element's value, we use this form:
ARRAYNAME[ INDEX ] = VALUE;
Our array is the m_array
, the value will be the newItem
, and the index
will be the next available space… which also happens to be the value of m_itemCount
.
After adding the item to the array, make sure to increment m_itemCount
by 1 as well.
6.5. void SmartFixedArray<T>::PopBack()
The PopBack function will remove the element currently at the end of the list of items in the array.
Error check: If the array is empty, we cannot remove any items. Throw an exception in this case.
We are one again lazy deleting. To do this, we just decrement m_itemCount
by 1.
6.6. T& SmartFixedArray<T>::GetAt( int index )
This function will return the element of the array at the index
position.
Error check: If the array is empty, throw an exception; we cannot return anything!
Error check: If the index is invalid (less than 0 or greater than or equal to the m_itemCount
), then throw an exception.
Otherwise, return the element of m_array
at the index
provided.
6.7. Unit tests
Once finished, run the program. Unit tests will run. Make sure everything is passing.
7. Example completed output
TEMPLATE EXERCISE -------------------- Test_Add -------------------- [FAIL] Add two floats and check the result * input_a = 2.5 * input_b = 3.25 * expected_result = 5.75 * actual_result = -0.75 [FAIL] Add two ints and check the result * input_a = 3 * input_b = 7 * expected_result = 10 * actual_result = -4 Press ENTER to continue... -------------------- Test_Display (Manual tests) -------------------- Test 1 - Classes Class 1 Class 2 Class 3 Class 4 -------------------------------------------------------------------------------- CS 134 CS 200 CS 235 CS 250 ASL 120 ASL 122 ASL 135 ASL 145 Test 2 - Minimum wage vs. Median monthly rent 2010 2000 1990 1980 1970 1960 1950 -------------------------------------------------------------------------------- 7.25 5.15 3.80 3.10 1.60 1.00 0.75 841.00 602.00 447.00 243.00 108.00 71.00 42.00 Press ENTER to continue... -------------------- Test_Log -------------------- [PASS] Log variable values and check results Input data: * data1 = 3 * data2 = 3.14 * data3 = cheese File contents: * buffer1 = data1: 3 * buffer2 = data2: 3.14 * buffer3 = data3: cheese Press ENTER to continue... Running testset 1 out of 6: Constructor() [PASS] 0a. Check if functions are implemented [PASS] 1. Check initial member variable values Running testset 2 out of 6: PopBack() [PASS] 0. Check if function PopBack is implemented [PASS] 1. Set up array, remove back [PASS] 2. ERROR CHECK: Check for exception with empty array Running testset 3 out of 6: GetAt() [PASS] 0. Check if function GetAt is implemented [PASS] 1. Set up array, get at 1 [PASS] 2. ERROR CHECK: Check for exception with invalid index [PASS] 3. ERROR CHECK: Check for exception when array is empty Running testset 4 out of 6: IsFull() [PASS] 0. Check if function IsFull is implemented [PASS] 1. Check if IsFull returns true when full [PASS] 2. Check if IsFull returns false when not full Running testset 5 out of 6: IsEmpty() [PASS] 0. Check if function IsEmpty is implemented [PASS] 1. Check if IsEmpty returns false when an item is in the array [PASS] 2. Check if IsEmpty returns true when empty Running testset 6 out of 6: PushBack() [PASS] 0a. Check if function PushBack is implemented [PASS] 1. Add three items, check they're in the correct position [PASS] 2. ERROR CHECK: Check if StructureFullException is thrown if array ... - RESULTS ------------------------------------------------------------ - Total tests: 6 - Total tests passing: 6 - Total tests failing: 0 - - NOTE: CHECK "test_result_smart_fixed_array.html" in the directory -- - - for full information on test results, - including expected output vs. actual output and notes. ----------------------------------------------------------------------
8. Turning in your work
- CS 235
- You can turn in your work by uploading your code files, or if you want to use a repository you can also submit the URL to where your project is being stored.
- CS 250
- Make sure to
add
,commit
, andpush
your work to your u06ex branch.
Afterwards, go to the GitLab repository page and create a Merge Request. The URL of the merge request is what you will turn in on Canvas.