C++ Programming assignment

oliviabreeland
previousassignment.zip

C++ Program Exercise.docx

Programming Exercise #8

(Associative Arrays: Simple Implementation)

Should run on Centos Linux

.hpp should be included

main.cpp file should be included

Makefile should be included.

Overview

In this programming exercise the coder will create an associative array data structure. An associative array accepts a key and returns an associated value. It is an extremely useful data structure which may also be referred to as a map, dictionary, or symbol table. The first phase of this exercise deals with the interface and creating the correct behavior of the data structure. Follow on phases will make the data structure more efficient.

As a practical use of the data structure, the coder will construct a database of electronic components. Each of the components will have a number of features/specifications, and each feature/specification will have a value. For example, here are a couple of resistors that may be found in our parts database:

Name: Resistor#1

Temp coefficient: 50ppm/C

Frequency response: 10MHz

Power dissipation: 1W

Max temperature: 100C

Max voltage: 3.3V

Name: Resistor #2

Temp coefficient: 40ppm/C

Frequency response: 20MHz

Power dissipation: 5W

Max temperature: 120C

Max voltage: 5V

Parts Catalog Architecture

The parts catalog is an associative array of associative arrays. The outermost array is keyed by part name, (a string in this case). The content of the outer associative array is the inner associative array. The inner associative array is keyed by a parameter name and the content is the parameter value, (both strings).

When iterating through the structure it should be possible to print all part names in order and then print each parameter and associated value for that part in order of parameter name. It should also be possible to extract any given parameter value by using the get methods.

Programming Concepts

This exercise covers many programming concepts including inheritance, deep copy, constructor types including default and delete, operator overloading, pass by ref and by val, return by ref, stack vs heap, linked lists, templates, reference counting pointers, lambdas, closures, functors, and abstract base classes.

System Requirements

The design must use the provided interface header verbatim. This will allow automated testing of the design you produce. See grading rubric for specific system requirements and associated grade values.

Grading Rubric

(PEX8)

Requirement / Criteria

Available Points

Student’s Score

Uses base class interface verbatim

10

Can be created on the stack

10

Is copy constructible and assignable

10

Correctly deep copies in insert

10

Modification after “get” modifies content in structure

10

Modification in “forEach” modifies content in structure

10

Method “forEach” iterates in order

10

Inserting/deleting at head of list functions correctly

10

“getting” and “deleting” non-existent key functions correctly

10

System is free of memory leaks

10

Total

100

__MACOSX/._C++ Program Exercise.docx

KeyVal.hpp

#ifndef KEYVAL_HPP #define KEYVAL_HPP #include <memory> #include <functional> /** * @class KeyVal * * This class is an interface to a system that can * arbitrarily track key/value pairs. The abiltiy to * build an "associative array" will be invaluable * to a coder * * An associative array allows the coder to fetch * an object by using a key. This essentially makes * a content addressable data structure. * * All derived classes must use this template verbatim * * Notes: * 1. Objects that are used as keys in the table * must have the less than operator defined. * 2. KeyVal objects should be creatable on both the * stack and the heap * 3. KeyVal objects should be assignable and copy constructible * and should make deep copies as appropriate * 4. The forEach method should iterate over the structure in * sorted order least to greatest according to key value */ template<class K, class V> class KeyVal { public: /** * @brief Insert an object * * This will place a COPY of the val object * into the associative array * * @param key Key associated with value * @param val Value which is stored at location key */ virtual void insert(const K &key, const V &val) = 0; /** * @brief Remove an object from the associative array * * This will remove the key/value pair from the array * If the key is not found, no action is taken * * @param key Key for which key/val pair is removed */ virtual void del(const K &key) = 0; /** * @brief Get a pointer to value * * Given a key, a shared_ptr to a value is returned. * note that if the key did not exist, then the returned * ptr is not valid * * @param key Key for which value is returned * @return ptr to value if key existed */ virtual std::shared_ptr<V> get(const K &key) = 0; /** * @brief Execute callback for each entry * * Rather than force students to create an inner iterator class, * this functiona allows a callback function to be executed for * every item in the associative array. Note that callbacks should * be called in order of keys sorted least to greatest * * Note that keys are passed by const ref; they cannot be changed, yet * values are passed by non-const ref. They can be changed and that change * should be reflected in the underlying data structure. * * @param callback Function to be called with each item in the associative array */ virtual void forEach(std::function<void(const K &key, V &val)> callback) = 0; }; #endif /* KEYVAL_HPP */

__MACOSX/._KeyVal.hpp

ListKeyVal.hpp

#ifndef LISTKEYVAL_HPP #define LISTKEYVAL_HPP #include "KeyVal.hpp" #include <iostream> /********************************************************************* * This is an example header without any implementation that you * may find useful in your effort to create an associative array. * * You are not required to conform to this header in any way, you are * only required to conform to the overall interface header provided * in KeyVal.hpp. * * However, there are helpful hints contained here that may make both * this PEX and future PEXes which build on the base interface easier * for you to implement. A careful study of this architecture is * highly recommended. * ********************************************************************/ //forward declaration template<class K, class V> class ListKeyVal; /** * @class ListKeyValNode * * The purpose of this class is to store key value pairs in a sorted list. * Note that all methods and members are private and only accessible by the * friend class ListKeyVal. * * The reason that the ListKeyValNode class and the ListKeyVal class are * separate is to make usage cleaner. We must be able to create ListKeyVal * objects on the stack just as we would any other object. Since additions and * deletions to the list may change the root node, it is not effective to * create a root node as the base object * * This separate private class also allows us to remove some default constuctors * and use only deep copy assignment in this class while exposing all options * in the public class. */ template<class K, class V> class ListKeyValNode : public std::enable_shared_from_this<ListKeyValNode<K, V> > { friend class ListKeyVal<K, V>; private: /** * @brief Default Constructor */ ListKeyValNode() = default; /** * @brief Remove the copy constructor */ ListKeyValNode(const ListKeyValNode &other) = delete; /** * @brief provide an assignment operator which deep copies our * data structure * * @param other Ref to RHS object; deep copy this structure * @return ListKeyNodeVal ref for chaining assignments */ ListKeyValNode &operator=(const ListKeyValNode &other); /** @brief Ptr to key; may be null for last item in list */ std::shared_ptr<K> m_key; /** @brief Ptr to value; may be null for last item in list */ std::shared_ptr<V> m_val; /** @brief Ptr to next node in list; may be null for last item in list */ std::shared_ptr<ListKeyValNode> m_next; /** @brief Weak ptr to prev node in list; may be null for first item in list note that weak ref is used to avoid mem leak islands*/ std::weak_ptr<ListKeyValNode<K, V> > m_prev; }; /** * @class ListKeyVal */ template<class K, class V> class ListKeyVal : public KeyVal<K, V> { public: /** * @brief Constructor * * This ctor creates a valid root node */ ListKeyVal(); /** * @brief Copy ctor * * Creates a deep copy of entire data structure * * @param other Data structure to copy */ ListKeyVal(const ListKeyVal &other); /** * @brief Assignment operator * * Creates a deep copy of entire data structure * * @param other Data structure to copy * @return ref to populated object for assignment chaining */ ListKeyVal &operator=(const ListKeyVal &other); /** * @brief Insert an object * * This will place a COPY of the val object * into the associative array * * Note that since an insert may change the root node, I have * created an "internal" function that returns the new root. * this function will call the internal version and then reset * the root node if needed. This model will make follow on * PEXs where recursion is required more clean/understandable. * * @param key Key associated with value * @param val Value which is stored at location key */ virtual void insert(const K &key, const V &val) override; /** * @brief Remove an object from the associative array * * This will remove the key/value pair from the array * If the key is not found, no action is taken * * Note that since a delete may change the root node, I have * created an "internal" function that returns the new root. * this function will call the internal version and then reset * the root node if needed. This model will make follow on * PEXs where recursion is required more clean/understandable. * * @param key Key for which key/val pair is removed */ virtual void del(const K &key) override; /** * @brief Get a pointer to value * * Given a key, a shared_ptr to a value is returned. * note that if the key did not exist, then the returned * ptr is not valid * * @param key Key for which value is returned * @return ptr to value if key existed */ virtual std::shared_ptr<V> get(const K &key) override; /** * @brief Execute callback for each entry * * Rather than force students to create an inner iterator class, * this functiona allows a callback function to be executed for * every item in the associative array. Note that callbacks should * be called in order of keys sorted least to greatest * * @param callback Function to be called with each item in the associative array */ virtual void forEach(std::function<void(const K &key, V &val)> callback) override; private: /** * @brief Insert a node and return new root * * @param key Key to insert * @param val Value associated with key * @return New root of node list */ std::shared_ptr<ListKeyValNode <K, V> > insertInternal(const K &key, const V &val); /** * @brief Delete a node and return new root * * @param key Key to insert * @return New root of node list */ std::shared_ptr<ListKeyValNode <K, V> > delInternal(const K &key); /** @breif Track root node */ std::shared_ptr<ListKeyValNode<K, V> > m_rootNode; }; #endif /* LISTKEYVAL */

__MACOSX/._ListKeyVal.hpp

main.cpp

#include "ListKeyVal.hpp" #include <string> #include <iostream> int main() { ListKeyVal<std::string, ListKeyVal<std::string, int> > outer; ListKeyVal<std::string, int> lkv; //populate an LKV lkv.insert("one", 1); lkv.insert("three", 3); lkv.insert("four", 4); lkv.insert("two", 2); //put two copies map into outer outer.insert("outer1", lkv); outer.insert("outer2", lkv); //iterate over outer then inner std::cout << std::endl << std::endl << "Full iteration:" << std::endl; outer.forEach([](const std::string &key, ListKeyVal<std::string, int> &val){ std::cout << key << std::endl; val.forEach([](const std::string &key, int &val){ std::cout << " Key: " << key << std::endl; std::cout << " Val: " << val << std::endl; }); }); //make a copy of one LKV, delete an item, then iterate std::cout << std::endl << std::endl << "Deleted item \"three\":" << std::endl; std::shared_ptr<ListKeyVal<std::string, int> > pLkv = outer.get("outer1"); pLkv->del("three"); pLkv->forEach([](const std::string &key, int &val){ std::cout << " Key: " << key << std::endl; std::cout << " Val: " << val << std::endl; }); //increment each val in second lkv pLkv = outer.get("outer2"); pLkv->forEach([](const std::string &key, int &val){ val++; }); //final iteration, should contain three in first set and //each val in second should be incremented std::cout << std::endl << std::endl << "After each item incremented:" << std::endl; outer.forEach([](const std::string &key, ListKeyVal<std::string, int> &val){ std::cout << key << std::endl; val.forEach([](const std::string &key, int &val){ std::cout << " Key: " << key << std::endl; std::cout << " Val: " << val << std::endl; }); }); }

__MACOSX/._main.cpp

testall.sh

#!/bin/bash if [[ $# -ne 1 ]] ; then echo "Usage: testall <NameOfClass>" exit 1 fi #make include files echo "#include \"../$1.hpp\"" > include.hpp echo "$1<std::string, $1<std::string, int> > outer;" > instantiate.hpp echo "$1<std::string, int> inner;" >> instantiate.hpp echo "std::shared_ptr<KeyVal<std::string, int> > pKeyVal(new $1<std::string, int>); " >> instantiate.hpp #compile and execute first test (interface conformance) g++ -g -std=c++11 test1.cpp -o test1 2>/dev/null if [[ $? -ne 0 ]] ; then echo "Failed interface verbatim test during compilation" else ./test1 | grep one1three4 > /dev/null && echo "Passed interface verbatim test" || echo "Failed interface verbatim test" fi #compile and execute second test (stack creation) g++ -g -std=c++11 test2.cpp -o test2 2>/dev/null if [[ $? -ne 0 ]] ; then echo "Failed stack constuction compilation test" else ./test2 | grep success > /dev/null && echo "Passed stack construction test" || echo "Failed stack construction test" fi #compile and execute third test (copy construct and assign) g++ -g -std=c++11 test3.cpp -o test3 2>/dev/null if [[ $? -ne 0 ]] ; then echo "Failed copy/assign compilation test" else ./test3 | grep success > /dev/null && echo "Passed cp ctor and assign test" || echo "Failed cp ctor and assign test" fi #compile and execute fourth test (deep copy) g++ -g -std=c++11 test4.cpp -o test4 if [[ $? -ne 0 ]] ; then echo "Failed deep copy compilation test" else ./test4 | grep outer1two2outer2one1 > /dev/null && echo "Passed deep copy test" || echo "Failed deep copy test" fi #compile and execute fifth test (modify after get) g++ -g -std=c++11 test5.cpp -o test5 if [[ $? -ne 0 ]] ; then echo "Failed get/modify compilation test" else ./test5 | grep 3 > /dev/null && echo "Passed get/modify test" || echo "Failed get/modify test" fi #compile and execute sixth test (modify in foreach) #doesn't check ordering g++ -g -std=c++11 test6.cpp -o test6 if [[ $? -ne 0 ]] ; then echo "Failed modify in foreach compilation test" else ./test6 | grep 2 > /dev/null if [[ $? -eq 0 ]] ; then ./test6 | grep 3 > /dev/null if [[ $? -eq 0 ]] ; then ./test6 | grep 4 > /dev/null if [[ $? -eq 0 ]] ; then echo "Passed foreach modify test" else echo "Failed foreach modify test" fi else echo "Failed foreach modify test" fi else echo "Failed foreach modify test" fi fi #compile and execute seventh test (iterate in order) g++ -g -std=c++11 test7.cpp -o test7 if [[ $? -ne 0 ]] ; then echo "Failed in order iteration compilation test" else ./test7 | grep 132 > /dev/null && echo "Passed in order iteration test" || echo "Failed in order itration test" fi #compile and execute eighth test (insert at head) #doesn't check ordering g++ -g -std=c++11 test8.cpp -o test8 if [[ $? -ne 0 ]] ; then echo "Failed insert at head compilation test" else ./test8 | grep 1 > /dev/null if [[ $? -eq 0 ]] ; then ./test8 | grep 2 > /dev/null if [[ $? -eq 0 ]] ; then ./test8 | grep 3 > /dev/null if [[ $? -eq 0 ]] ; then echo "Passed insert at head test" else echo "Failed insert at head test" fi else echo "Failed insert at head test" fi else echo "Failed insert at head test" fi fi #compile and execute ninth test (get/del non-key) g++ -g -std=c++11 test9.cpp -o test9 if [[ $? -ne 0 ]] ; then echo "Failed non existing key compilation test" else ./test9 | grep success > /dev/null && echo "Passed non-key test" || echo "Failed non-key test" fi #mem leak test #test1 should be a sufficient test for mem leaks valgrind ./test1 2>&1 | grep "no leaks are possible" > /dev/null && echo "Passed leak test" || echo "Failed leak test"

__MACOSX/._testall.sh

tests/.DS_Store

__MACOSX/tests/._.DS_Store

tests/include.hpp

#include "../ListKeyVal.hpp"

__MACOSX/tests/._include.hpp

tests/instantiate.hpp

ListKeyVal<std::string, ListKeyVal<std::string, int> > outer; ListKeyVal<std::string, int> inner; std::shared_ptr<KeyVal<std::string, int> > pKeyVal(new ListKeyVal<std::string, int>);

__MACOSX/tests/._instantiate.hpp

tests/test1.cpp

#include <iostream> #include "include.hpp" int main() { #include "instantiate.hpp" inner.insert("one", 1); inner.insert("two", 2); inner.insert("three", 3); inner.del("two"); *(inner.get("three")) = 4; inner.forEach([](const std::string &key, int &val){ std::cout << key << val; }); }

__MACOSX/tests/._test1.cpp

tests/test2.cpp

#include <iostream> #include "include.hpp" int main() { #include "instantiate.hpp" std::cout << "success" << std::endl; }

__MACOSX/tests/._test2.cpp

tests/test3.cpp

#include <iostream> #include "include.hpp" int main() { #include "instantiate.hpp" //copy ctor auto newObj = outer; //assign operator newObj = outer; std::cout << "success" << std::endl; }

__MACOSX/tests/._test3.cpp

tests/test4.cpp

#include <iostream> #include "include.hpp" int main() { #include "instantiate.hpp" //populate inner.insert("one", 1); inner.insert("two", 2); outer.insert("outer1", inner); outer.insert("outer2", inner); inner.del("one"); inner.del("two"); outer.get("outer1")->del("one"); outer.get("outer2")->del("two"); outer.forEach([](const std::string &key, KeyVal<std::string, int> &val){ std::cout << key; val.forEach([](const std::string &key, int &val){ std::cout << key << val; }); }); }

__MACOSX/tests/._test4.cpp

tests/test5.cpp

#include <iostream> #include "include.hpp" int main() { #include "instantiate.hpp" //populate inner.insert("one", 1); inner.insert("two", 2); *(inner.get("one")) = 3; std::cout << *(inner.get("one")); }

__MACOSX/tests/._test5.cpp

tests/test6.cpp

#include <iostream> #include "include.hpp" int main() { #include "instantiate.hpp" inner.insert("one", 1); inner.insert("two", 2); inner.insert("three", 3); //increment val inner.forEach([](const std::string &key, int &val){ val++; }); //display inner.forEach([](const std::string &key, int &val){ std::cout << val; }); }

__MACOSX/tests/._test6.cpp

tests/test7.cpp

#include <iostream> #include "include.hpp" int main() { #include "instantiate.hpp" inner.insert("one", 1); inner.insert("two", 2); inner.insert("three", 3); //display inner.forEach([](const std::string &key, int &val){ std::cout << val; }); }

__MACOSX/tests/._test7.cpp

tests/test8.cpp

#include <iostream> #include "include.hpp" int main() { #include "instantiate.hpp" //each insert is at head inner.insert("two", 2); inner.insert("three", 3); inner.insert("one", 1); //display inner.forEach([](const std::string &key, int &val){ std::cout << val; }); }

__MACOSX/tests/._test8.cpp

tests/test9.cpp

#include <iostream> #include "include.hpp" int main() { #include "instantiate.hpp" //populate inner.insert("one", 1); inner.insert("two", 2); inner.del("two"); inner.del("two"); //second delete; key doesn't exist auto ptr = inner.get("two"); if(!ptr) std::cout << "success"; }

__MACOSX/tests/._test9.cpp