In this workshop, you design and code several class templates and test them on different instantiations.
Upon successful completion of this workshop, you will have demonstrated the abilities to:
- design and code a class template
- template a class variable
- specialize a templated class variable for a particular type
- instantiate a template class
- specialize a member function of a templated class to process a particular type
- derive a templated class from another templated class
The workshop is divided into two coding parts and one non-coding part:
- Part 1: worth 0% of the workshop's total mark, is optional and designed to assist you in completing the second part.
- Part 2: worth 100% of the workshop's total mark, is due on Sunday at 23:59:59 of the week of your scheduled lab. Submissions of Part 2 that do not contain the reflection are not considered valid submissions and are ignored.
- reflection: non-coding part, to be submitted together with Part 2. The reflection does not have marks associated to it, but can incur a penalty of max 40% of the whole workshop's mark if your professor deems it insufficient (you make your marks from the code, but you can lose some on the reflection).
The workshop should contain only work done by you this term or provided by your professor. Work done in another term (by you or somebody else), or work done by somebody else and not clearly identified/cited is considered plagiarism, in violation of the Academic Integrity Policy.
Every file that you submit must contain (as a comment) at the top your name, your Seneca email, Seneca Student ID and the date when you completed the work.
-
If the file contains only your work, or work provided to you by your professor, add the following message as a comment at the top of the file:
I have done all the coding by myself and only copied the code that my professor provided to complete my workshops and assignments.
-
If the file contains work that is not yours (you found it online or somebody provided it to you), write exactly which parts of the assignment are given to you as help, who gave it to you, or which source you received it from. By doing this you will only lose the mark for the parts you got help for, and the person helping you will be clear of any wrong doing.
All your code should be compiled using this command on matrix
:
/usr/local/gcc/10.2.0/bin/g++ -Wall -std=c++17 -g -o ws file1.cpp file2.cpp ...
-Wall
: compiler will report all warnings-std=c++17
: the code will be compiled using the C++17 standard-g
: the executable file will contain debugging symbols, allowing valgrind to create better reports-o ws
: the compiled application will be namedws
After compiling and testing your code, run your program as following to check for possible memory leaks (assuming your executable name is ws
):
valgrind ws
To check the output, use a program that can compare text files. Search online for such a program for your platform, or use diff available on matrix
.
This workshop consists of five modules:
w3
(supplied)Set
Pair
SetSummable
PairSummable
The solution to Part 1 invokes the first three modules.
Enclose all your source code within the sdds
namespace and include the necessary guards in each header file.
All the modules you create in this workshop must have only a header file. 🗎 Explain in the reflection why we do not split a module into *.h
and *.cpp
like you did in the previous workshops.
In all classes that you create, you are allowed to add any private members that your design requires (without changing the specs)!
Do not modify this module! Look at the code and make sure you understand how to instantiate a templated class.
This module represents a family of collections of elements of any data type (for example, sets of int
s, or sets of Student
s, etc.).
Design and code a class template named Set
. Your template manages a statically allocated array of any datatype. The template parameters in order of their specification are:
N
: the capacity of the collection (a non-type parameter; an integer without sign). This is the maximum number of elements that can be added to the collectionT
: the type of any element in the collection
Your design keeps track of the current number of elements stored in the collection (which may differ from the capacity of the collection (N
)). Initially the collection has no elements.
This module should not use or know the type Pair
!!
Public Members
size_t size() const
: returns the current number of elements in the collectionconst T& get(size_t idx) const
: returns a reference to the element at indexidx
of the statically allocated array (assume that the parameter is valid).void operator+=(const T& item)
: if the collection has capacity for another element, adds a copy ofitem
to the collection. Otherwise, does nothing.
Add any other private members that your design requires (without changing the specs above)!
This module represents a family of value-key pairs.
Design and code a class template named Pair
. Your template manages a single value-key pair. The template parameters (in order) identify the type of the value and the type of the key that constitute a Pair
object:
V
: the type of the valueK
: the type of the key
This module should not use or know the type Set
!!
Public Members
- default constructor
Pair(const K& key, const V& value)
: copies the values referred to by the parameters into the instance variablesconst V& value() const
: returns the value component of the pairconst K& key() const
: returns the key component of the pairvoid display(std::ostream& os) const
: inserts into streamos
the key and the value of the pair in the following formatKEY : VALUE<endl>
Free Helper
std::ostream& operator<<(std::ostream& os, const Pair<V, K>& pair)
: calls the member functiondisplay()
onpair
to insert a pair into streamos
.
Add any other private members that your design requires (without changing the specs above)!
When the program is started with the command (the file sales.txt
is provided):
ws sales.txt
the output should look like the one from the sample_output.txt
file.
To test the execution of your program, use the same data as shown in the output example above.
Upload your source code to your matrix
account. Compile and run your code using the latest version of the g++
compiler (available at /usr/local/gcc/10.2.0/bin/g++
) and make sure that everything works properly.
Then, run the following command from your account (replace profname.proflastname
with your professor’s Seneca userid):
~profname.proflastname/submit 345_w3_p1
and follow the instructions.
This part represents a milestone in completing the workshop and is not marked!
The second part of this workshop upgrades your Part 1 solution to
- align the key and value output in pretty columnar format
- accumulate the values stored in a
Set
object, for a specified key, which serves as the filter for selecting elements from theSet
To implement each upgrade, you will derive templated classes from your original templated classes (one derived class from Set
and one derived class from Pair
) and specialize the class derived from Pair
as described below.
Modify the display()
member function in the Pair
module to enable inclusion polymorphism on the Pair
hierarchy.
No other changes are necessary to this module.
A new module called PairSummable
represents a Pair
that has key alignment, compatibility and summation functionality.
Derive the PairSummable
class template from Pair<V, K>
. Your template receives 2 template parameters:
V
: the type of the valueK
: the type of the key
This module should not use or know the type Set
or SetSummable
!!
Static Private Members for PairSummable
-
an object of type
V
that holds the initial value for a summation. The initial value varies with the type of the value in the value-key pair. -
a variable of type
size_t
that holds the minimum field width for pretty columnar output of key-value pairs (initialize it with 0). This is the minimum number of characters needed to display any key in a set of keys.This value must be updated every time a new pair is constructed.
Public Members for PairSummable
-
default constructor
-
PairSummable(const K& key, const V& value = initial)
: calls the base class constructor passing the two parameters to it, and updates the field width if necessary. 🗎 Explain in the reflection what= initial
in the prototype means.- This function assumes that the type
K
supports a function namedsize()
, which returns the number of characters required to displaykey
. Use this function to determine if the field width must be updated.
- This function assumes that the type
-
bool isCompatibleWith(const PairSummable<V, K>& b) const
: returns true if the parameter has the samekey
as the current object, false otherwise. -
overload the
operator+=
to receive a reference to an unmodifiablePairSummable
object. This function adds thevalue
of the parameter object to thevalue
of the current object and returns a reference to the current object. Assume that the current object and the parameter have the samekey
. -
override the
display()
query to set the alignment to left and the field width to the value of the static attribute (see above, in the static members section) for allK
types, then call the base class version ofdisplay()
, and finally restore the alignment to right.
Specializations
- for
V = std::string
andK = std::string
, the functionoperator+=()
should concatenate the values stored using", "
as a separator (use operator+
to concatenate strings), for the result shown in the sample output.
The Set
module doesn't require any change.
A new module called SetSummable
represents a collection that is summable on a subset of the collection.
Derive the SetSummable
class template from your Set<N, T>
template. Your new template has 2 template parameters (in order):
N
: the capacity of the collection (an integer without sign)T
: the type of any element in the collection
Your design assumes that the type T
:
- has a constructor that accepts an
std::string
as a parameter, which specifies the filter for identifying the elements that belong to the subset. - has a member function named
isCompatibleWith
, which returns true if the object is compatible with an object of typeT
; false otherwise. - supports the
operator+=
operation, which adds an object of typeT
to another object of typeT
.
This module should not use or know the type Pair
or PairSummable
!!
Public Member for SetSummable
-
T accumulate(const std::string& filter) const
: this query accumulates into a local object of typeT
the subset of all the elements in the collection that satisfyfilter
.-
define a local
T
object that satisfiesfilter
, using the one-argument constructor. This object will serve as the accumulator. -
iterate over the collection and add to your accumulator only those elements that are compatible with the local object.
- check each element in the collection to determine if it is compatible with your accumulator object (use the
this->get()
member function to access the element) - if compatible, add the element to your accumulator object
- check each element in the collection to determine if it is compatible with your accumulator object (use the
-
return a copy of your accumulator object to the client.
-
In our very specific instantiation, SetSummable
will manage a collection of PairSummable
objects; two objects of type PairSummable
are considered compatible if the have the same key. The design above is more general, and allows SetSummable
to work with other types that are not PairSummable
as long as they support the mentioned operations.
When the program is started with the command (the files sales.txt
and products.txt
are provided):
ws products.txt sales.txt
the output should look like the one from the sample_output.txt
file.
Study your final solution, reread the related parts of the course notes, and make sure that you have understood the concepts covered by this workshop. This should take no less than 30 minutes of your time and the result is suggested to be at least 150 words in length.
Create a text file named reflect.txt
that contains your detailed description of the topics that you have learned in completing this workshop and mention any issues that caused you difficulty and how you solved them. Include in your explanation—but do not limit it to—the following points:
- the reason for specializing the
operator+=()
member function. - why we don't need to specialize the initial value for the summation (see that the value is different when we add numbers versus when we add strings).
- the reason for defining the class variable outside the class definition.
- answers to the other questions in these specifications.
To avoid deductions, refer to code in your solution as examples to support your explanations.
To test and demonstrate execution of your program use the same data as shown in the output example above.
Upload the source code and the reflection file to your matrix
account. Compile and run your code using the latest version of the g++
compiler (available at /usr/local/gcc/10.2.0/bin/g++
) and make sure that everything works properly.
Then, run the following command from your account (replace profname.proflastname
with your professor’s Seneca userid):
~profname.proflastname/submit 345_w3_p2
and follow the instructions.