C++ Program with Pointers
Pointers/bodgeUnitTest.h
#ifndef BODGE_UNIT_TEST_H_INCLUDED #define BODGE_UNIT_TEST_H_INCLUDED #include <cstdlib> #include <functional> #include <iostream> #include <string> #include "utilities.h" #include "bodgeUnitTest.h" /** * This is the Bodge-Unit-Testing... PseUdO-Framework * * Bodge - A clumsy or inelegant job, usually a temporary repair; * a patch, a repair. (From Wiktionary) */ #define bodgeAssert(expression) \ if (!(expression)) { \ std::cout << "FAILURE: "\ << __func__ << ":" << __LINE__\ << " -> (" << #expression << ")\n";\ return false;\ } // End Macro // Unit Aliases using UnitTestFunction = std::function<bool()>; using UnitTestPair = std::pair<UnitTestFunction, std::string>; /** * Run a single unit test function and output PASSED of FAILED based on the * result. * * @TODO I could (and should) probably turn this into a macro. */ inline void runTest(const UnitTestFunction& testFunction, std::string description) { std::cout << " " << (testFunction() ? "PASSED" : "FAILED") << " -> " << description << std::endl; } #endif
Pointers/CPPLINT.cfg
linelength=80 filter=-legal/copyright filter=-readability/namespace filter=-build/header_guard filter=-whitespace/indent filter=-whitespace/braces filter=-whitespace/blank_line filter=-build/namespaces filter=-readability/braces filter=-whitespace/newline filter=-build/include_subdir filter=-runtime/references filter=-runtime/threadsafe_fn filter=-runtime/int filter=-runtime/explicit filter=-whitespace/ending_newline filter=-runtime/string # Too many false positives filter=-build/include
Pointers/Inventory.cpp
#include <utility> #include <algorithm> #include "Inventory.h" // Allow the compiler to define the remaining // comparison operators using namespace std::rel_ops; //------------------------------------------------------------------------------ Inventory::Inventory() :Inventory(10) { } //------------------------------------------------------------------------------ Inventory::Inventory(int n) :slots(n) { } //------------------------------------------------------------------------------ Inventory::Inventory(const Inventory& src) { this->slots = src.slots; this->allItemStacks.reserve(this->slots); std::copy(src.begin(), src.end(), std::back_inserter(this->allItemStacks)); } //------------------------------------------------------------------------------ int Inventory::utilizedSlots() const { return allItemStacks.size(); } //------------------------------------------------------------------------------ int Inventory::emptySlots() const { return totalSlots() - utilizedSlots(); } //------------------------------------------------------------------------------ int Inventory::totalSlots() const { return slots; } //------------------------------------------------------------------------------ bool Inventory::isFull() const { return emptySlots() == 0; } //------------------------------------------------------------------------------ bool Inventory::addItems(ItemStack itemStack) { const int& targetId = itemStack.getItem().getID(); auto id_equal_function = [targetId](const ItemStack& aStack) { return aStack.getItem().getID() == targetId; }; auto matchingIterator = std::find_if(this->begin(), this->end(), id_equal_function); // A match was found if (matchingIterator != this->end()){ ItemStack& stackToUpdate = *matchingIterator; stackToUpdate.addItemsFrom(itemStack); return true; } // There is no space for a new type of `ItemStack` if (this->isFull()) { return false; } // This is a new type of item and there is plenty of room allItemStacks.push_back(itemStack); return true; } //------------------------------------------------------------------------------ Inventory::iterator Inventory::begin() { return allItemStacks.begin(); } //------------------------------------------------------------------------------ Inventory::iterator Inventory::end() { return allItemStacks.end(); } //------------------------------------------------------------------------------ Inventory::const_iterator Inventory::begin() const { return allItemStacks.begin(); } //------------------------------------------------------------------------------ Inventory::const_iterator Inventory::end() const { return allItemStacks.end(); } //------------------------------------------------------------------------------ void Inventory::display(std::ostream &outs) const { outs << " -Used " << utilizedSlots() << " of " << slots << " slots" << "\n"; for (const ItemStack& it : *this) { outs << " " << it << "\n"; } } //------------------------------------------------------------------------------ Inventory& Inventory::operator=(Inventory rhs) { std::swap(*this, rhs); return *this; } //------------------------------------------------------------------------------ void Inventory::swap(Inventory& other) { using std::swap; Inventory& lhs = *this; Inventory& rhs = other; swap(lhs.allItemStacks, rhs.allItemStacks); swap(lhs.slots, rhs.slots); } //------------------------------------------------------------------------------ bool operator==(const Inventory& lhs, const Inventory& rhs) { if (lhs.utilizedSlots() != rhs.utilizedSlots()) { return false; } if (lhs.totalSlots() != rhs.totalSlots()) { return false; } return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } //------------------------------------------------------------------------------ // This function is missing. While you do not have to write it... you need to // be able to explain: // // 1. Why the program still works without operator< // 2. Why not including operator< *can* lead to issues // // bool operator<(const Inventory& lhs, const Inventory& rhs)
Pointers/Inventory.h
#ifndef INVENTORY_H_INCLUDED #define INVENTORY_H_INCLUDED #include <iostream> #include <vector> #include "ItemStack.h" /** * An Inventory is composed of n slots. Each slot may store only * one type of item--specified by *slots*. * <p> * Once all slots are filled, no additional Item types may be stored. * Individual slots may contain any number of the same * Item. */ class Inventory { public: /** * Alias for a container of ItemStacks. */ using ItemStackCollection = std::vector<ItemStack>; using iterator = ItemStackCollection::iterator; using const_iterator = ItemStackCollection::const_iterator; private: /** * All `ItemStack`s in *this* `Inventory` */ ItemStackCollection allItemStacks; /** * Total capacity of *this* `Inventory`. */ int slots; public: /** * Default to 10 slots */ Inventory(); /** * Create an inventory. * * @param n total slots (capacity) * * @pre n > 0 */ Inventory(int n); /** * Duplicate an existing Inventory * * @param src existing Inventory. */ Inventory(const Inventory& src); /** * Empty all Inventory slots. * * Let the compiler do all the work; */ ~Inventory() = default; /** * Check the number of used/utilized (i.e., non-empty). */ int utilizedSlots() const; /** * Check the number of unused (i.e., empty) slots. */ int emptySlots() const; /** * Retrieve the total size (number of slots in total). */ int totalSlots() const; /** * Check if this inventory is full * * @return true if all slots are filled and false otherwise */ bool isFull() const; /** * Add one or more items to the inventory list * * @return true if *stack* was added and false otherwise */ bool addItems(ItemStack itemStack); /** * Print a Summary of the Inventory and all Items contained within */ void display(std::ostream& outs) const; // Begin Iterator Support (begin/end) iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; // End Iterator Support /** * Assignment operator (implemented using the copy-and-swap trick). */ Inventory& operator=(Inventory rhs); /** * Swap the contents of *this* and another inventory. */ void swap(Inventory& other); }; /** * Print the Inventory through use of the display member function */ inline std::ostream& operator<<(std::ostream& outs, const Inventory& prt) { prt.display(outs); return outs; } /** * Compare two Inventory objects, without direct access */ bool operator==(const Inventory& lhs, const Inventory& rhs); /** * Swap the contents of two inventories. This is a wrapper around the swap * member function. * * @param lhs first inventory (left hand side) * @param rhs second inventory (right hand side) */ inline void swap(Inventory& lhs, Inventory& rhs) { lhs.swap(rhs); } #endif
Pointers/inventoryList-00.txt
# 3 - 1 10 - 2 5 - 3 2 # 6 - 4 3 - 5 27 - 6 44 - 7 55 - 8 1 - 9 4 - 4 3 # 5 - 2 5 - 9 4 - 8 1 - 5 2 - 7 5
Pointers/inventoryList-01.txt
# 5 - 1 10 - 2 5 - 3 2 # 6 - 4 3 - 5 27 - 6 44 - 7 55 - 8 1 - 9 4 - 4 3 # 2 - 2 5 - 9 4 - 8 1 - 5 2 - 10 5
Pointers/inventoryList-02.txt
# 2 - 1 10 - 57 1 - 57 4 # 3 - 4 3 - 13 27 - 13 44 - 17 55 - 17 1 - 19 4 - 42 3 # 2 - 41 5 - 49 4 - 49 1 - 264 2 - 33 5
Pointers/inventoryList-03.txt
# 2 - 1 10 - 57 1 - 57 4 # 3 - 4 3 - 13 27 - 13 44 - 17 55 - 17 1 - 19 4 - 42 3 # 2
Pointers/Item.cpp
#include <iomanip> #include "Item.h" #include "utilities.h" //------------------------------------------------------------------------------ Item::Item() :Item(0, "Air") { } //------------------------------------------------------------------------------ Item::Item(int i, std::string n) :id(i), name(n) { } //------------------------------------------------------------------------------ bool Item::operator==(const Item &rhs) const { return this->id == rhs.id; } //------------------------------------------------------------------------------ bool Item::operator<(const Item &rhs) const { return this->id < rhs.id; } //------------------------------------------------------------------------------ void Item::display(std::ostream &outs) const { outs << std::right << std::setw(3) << id << " " << name; } //------------------------------------------------------------------------------ void Item::read(std::istream& ins) { // Use temp vars in case something goes wrong... int i = 0; std::string n("Air"); ins >> i; getline(ins, n); trim(n); this->setID(i); this->setName(n); }
Pointers/Item.h
#ifndef ITEM_H_INCLUDED #define ITEM_H_INCLUDED #include <iostream> #include <string> /** * Item represents an individual Item in an inventory. * This includes items such as potions, building materials, and food. * * Only one of each item can exist--i.e., no two items share the * same numeric id. */ class Item { private: int id; ///< Unique numeric identifier--e.g., 1 std::string name; ///< Short title--e.g., HP Potion public: /** * Default to id = 0 and name = Air */ Item(); /** * Create an Item with a specified id and name * * @param i desired id * @param n desired name * * @pre * - all items that share an id are of the same type * - id is strictly positive */ Item(int i, std::string n); // The compiler can handle the Big-3 Item(const Item& src) = default; ~Item() = default; Item& operator=(const Item& rhs) = default; /** * Retrieve numeric id */ int getID() const; /** * Update numeric id * * @pre i is strictly positive */ void setID(int i); /** * Retrieve name */ std::string getName() const; /** * Update name */ void setName(std::string n); /** * Wrapper around copy constructor */ Item* clone() const; /** * Check for logical equivalence--based on numeric id */ bool operator==(const Item &rhs) const; /** * Check ordering--based on numeric id */ bool operator<(const Item &rhs) const; /** * Print one Item */ void display(std::ostream &outs) const; /** * Read in an update Item id and name */ void read(std::istream& ins); }; /** * Print one Item by invoking display. */ inline std::ostream& operator<<(std::ostream &outs, const Item &prt) { prt.display(outs); return outs; } /** * Read in one Item by invoking read. */ inline std::istream& operator>>(std::istream& ins, Item& toRead) { toRead.read(ins); return ins; } //------------------------------------------------------------------------------ inline int Item::getID() const { return this->id; } //------------------------------------------------------------------------------ inline void Item::setID(int i) { this->id = i; } //------------------------------------------------------------------------------ inline std::string Item::getName() const { return this->name; } //------------------------------------------------------------------------------ inline void Item::setName(std::string n) { this->name = n; } //------------------------------------------------------------------------------ inline Item* Item::clone() const { return new Item(*this); } #endif
Pointers/itemList-00.txt
0 Air 1 HP Potion 2 MP Potion 5 Iron Ore 3 Bow Tie 4 Dirt 6 Diamond Ore 7 Iron Ingot 8 Diamond 9 Diamond Block
Pointers/itemList-01.txt
0 Air 1 HP Potion 2 MP Potion 5 Iron Ore 3 Bow Tie 4 Dirt 6 Diamond Ore 7 Iron Ingot 8 Diamond 9 Diamond Block
Pointers/itemList-02.txt
0 Air 1 Stone 2 Grass 3 Dirt 4 Cobblestone 13 Gravel 14 Gold Ore 15 Iron Ore 16 Coal Ore 17 Oak Wood 19 Sponge 33 Piston 41 Gold Block 42 Iron Block 49 Obsidian 50 Torch 56 Diamond Ore 57 Diamond Block 73 Redstone Ore 264 Diamond
Pointers/ItemStack.cpp
#include <iomanip> #include "ItemStack.h" const Item ItemStack::DEFAULT_ITEM(0, "Air"); //------------------------------------------------------------------------------ ItemStack::ItemStack() :ItemStack(DEFAULT_ITEM, 0) { } //------------------------------------------------------------------------------ ItemStack::ItemStack(const Item& inputItem, int s) :quantity(s) { // Create a copy (clone) of inputItem and reference it with the this->item // pointer this->item = inputItem.clone(); } //------------------------------------------------------------------------------ ItemStack::ItemStack(const ItemStack& src) { // 1 . Create a copy (clone) of src.item and reference it with the // this->item pointer. // // 2. Do not forget to copy src.quantity. // } //------------------------------------------------------------------------------ ItemStack::~ItemStack() { // Every pointer must be deleted to prevent memory leaks (item is a pointer). } //------------------------------------------------------------------------------ ItemStack& ItemStack::operator=(ItemStack rhs) { swap(*this, rhs); return *this; } //------------------------------------------------------------------------------ Item ItemStack::getItem() const { return *(this->item); } //------------------------------------------------------------------------------ int ItemStack::size() const { return this->quantity; } //------------------------------------------------------------------------------ void ItemStack::addItems(int a) { this->quantity += a; } //------------------------------------------------------------------------------ void ItemStack::addItemsFrom(const ItemStack& other) { this->quantity += other.quantity; } //------------------------------------------------------------------------------ bool ItemStack::operator==(const ItemStack& rhs) const { // Compare this and rhs for equivalence based on the ids of this->item and // rhs.item. return false; // replace this line } //------------------------------------------------------------------------------ bool ItemStack::operator<(const ItemStack& rhs) const { // Order (sort) this and rhs based on the ids of this->item and rhs.item. return false; // replace this line } //------------------------------------------------------------------------------ void ItemStack::display(std::ostream& outs) const { outs << std::right << "(" << std::setw(2) << this->size() << ") " << (this->getItem()).getName(); } //------------------------------------------------------------------------------ void swap(ItemStack& lhs, ItemStack& rhs) { // Swap the item data members and quantity data members for lhs and rhs. }
Pointers/ItemStack.h
#ifndef ITEMSTACK_H_INCLUDED #define ITEMSTACK_H_INCLUDED #include <iostream> #include "Item.h" using namespace std::rel_ops; /** * A Homogeneous--i.e., uniform--stack of Items. */ class ItemStack { private: /** * Default Air item with id 0. */ static const Item DEFAULT_ITEM; /** * Item out of which the stack is composed. */ Item* item; /** * Number of items in the stack. */ int quantity; public: /** * Default to an empty stack composed of Air */ ItemStack(); /** * Create a stack of type *item* * * @param item Item out of which the stack is composed * @param s size of the stack * * @pre (s > 0) */ ItemStack(const Item& item, int s); // The Big-3 ItemStack(const ItemStack& src); ~ItemStack(); ItemStack& operator=(ItemStack rhs); // End the Big-3 /** * Retrieve the Item out of which the stack is composed */ Item getItem() const; /** * Retrieve the size of the stack */ int size() const; /** * Increase the size of the stack * * @param a number of items to add * @pre a > 0 */ void addItems(int a); /** * Increase the size of the stack * * @param other ItemStack with the items to move (i.e., steal). * * @pre *this.item == other.item */ void addItemsFrom(const ItemStack& other); /** * Consider two stacks to be the same if * they contain the same type of Item. */ bool operator==(const ItemStack& rhs) const; /** * Order stacks based on Item id. */ bool operator<(const ItemStack& rhs) const; /** * The usual display function. */ void display(std::ostream& outs) const; /** * The usual friend-ly swap function. */ friend void swap(ItemStack& lhs, ItemStack& rhs); }; /** * Print the ItemStack using the display function. */ inline std::ostream& operator<<(std::ostream& outs, const ItemStack& prt) { prt.display(outs); return outs; } #endif
Pointers/make.dep
storage.o: storage.cpp Inventory.h ItemStack.h Item.h utilities.h Item.o: Item.cpp Item.h utilities.h ItemStack.o: ItemStack.cpp ItemStack.h Item.h Inventory.o: Inventory.cpp Inventory.h ItemStack.h Item.h utilities.o: utilities.cpp utilities.h
Pointers/makefile
MAINPROG=storage CPPS= storage.cpp Item.cpp ItemStack.cpp Inventory.cpp utilities.cpp INVENTORY_TEST_CPPS= Item.cpp ItemStack.cpp Inventory.cpp TestInventory.cpp utilities.cpp ITEMSTACK_TEST_CPPS= Item.cpp ItemStack.cpp TestItemStack.cpp utilities.cpp DIR=${PWD} ASST=$(notdir ${DIR}) ifneq (,$(findstring MinGW,$(PATH))) DISTR=MinGW EXE=.exe LFLAGS= else DISTR=Unix EXE= LFLAGS=-fsanitize=leak,address -fuse-ld=gold endif # ######################################################################## # Macro definitions for "standard" C and C++ compilations # CPPFLAGS=-g -std=c++14 -D$(DISTR) -Wall -Wextra -Wpedantic CFLAGS=-g TARGET=$(MAINPROG)$(EXE) LINK=g++ $(CPPFLAGS) # CC=gcc CPP=g++ # # # In most cases, you should not change anything below this line. # # The following is "boilerplate" to set up the standard compilation # commands: # OBJS=$(CPPS:%.cpp=%.o) DEPENDENCIES = $(CPPS:%.cpp=%.d) INVENTORY_TEST_OBJS=$(INVENTORY_TEST_CPPS:%.cpp=%.o) ITEMSTACK_TEST_OBJS=$(ITEMSTACK_TEST_CPPS:%.cpp=%.o) %.d: %.cpp touch $@ %.o: %.cpp $(CPP) $(CPPFLAGS) -MMD -o $@ -c $*.cpp # # Targets: # all: $(TARGET) tests win: $(OBJS) $(LINK) $(FLAGS) -o $(TARGET) $(OBJS) $(TARGET): $(OBJS) $(LINK) $(FLAGS) -o $(TARGET) $(OBJS) $(LFLAGS) tests: $(INVENTORY_TEST_OBJS) $(ITEMSTACK_TEST_OBJS) $(LINK) $(FLAGS) -o testInventory$(EXE) $(INVENTORY_TEST_OBJS) $(LINK) $(FLAGS) -o testItemStack$(EXE) $(ITEMSTACK_TEST_OBJS) clean: -/bin/rm -f *.d *.o $(TARGET) testInventory$(EXE) testItemStack$(EXE) make.dep: $(DEPENDENCIES) -cat $(DEPENDENCIES) > $@ include make.dep
Pointers/storage.cpp
#include <algorithm> #include <cstdlib> #include <fstream> #include <iostream> #include <iterator> #include <memory> #include <string> #include <vector> #include "Inventory.h" #include "Item.h" #include "ItemStack.h" #include "utilities.h" using namespace std; /** * Read file containing the list of all possible items. * * *In the next assignment this will be a generalized template function. * * @param inf input buffer * * @return collection of valid items, sorted by numeric id */ vector<Item> parseItemList(istream& inf); /** * Read inventory file and create all Inventory instances. * * @param items collection of valid Item entries * * @pre items is non-empty */ vector<unique_ptr<Inventory>> parseInventoryFile(istream& inf, const vector<Item>& items); /** * Generate a summary of all valid items */ void printItems(const vector<Item>& items); /** * Generate a summary of Inventory utilization */ void printInventories(const vector<unique_ptr<Inventory>>& storage); /** * Assignment 1: Item Storage * * @param argv[1] items filename * @param argv[2] inventories filename */ int main(int argc, char** argv) { // Check Command Line Arguments if (argc != 3) { cerr << "Usage: " << argv[0] << " list_file inventory_file" << "\n"; return 1; } ifstream infile(argv[1]); if (!infile) { cerr << "Error: " << argv[1] << " could not be opened" << "\n"; return 2; } // Read listing of possible (i.e., valid) items vector<Item> validItems = parseItemList(infile); // Close and reset for the next file infile.close(); infile.clear(); infile.open(argv[2]); if (!infile) { cout << "Error: " << argv[2] << " could not be opened" << "\n"; return 3; } vector<unique_ptr<Inventory>> storage = parseInventoryFile(infile, validItems); infile.close(); printItems(validItems); printInventories(storage); return 0; } //------------------------------------------------------------------------------ vector<Item> parseItemList(istream& inf) { vector<Item> items; std::copy(istream_iterator<Item>(inf), istream_iterator<Item>(), back_inserter(items)); std::sort(items.begin(), items.end()); return items; } //------------------------------------------------------------------------------ vector<unique_ptr<Inventory>> parseInventoryFile(istream& inf, const vector<Item>& items) { vector<unique_ptr<Inventory>> storage; Inventory* inv = nullptr; // First two values on a line char leading_char; int num_1; cout << "Processing Log:" << "\n"; while (inf >> leading_char >> num_1) { if (leading_char == '#') { if (inv != nullptr) { storage.emplace_back(inv); } inv = new Inventory(num_1); } else { int quantity; inf >> quantity; // Search for (key) id in list of known items const int& key = num_1; vector<Item>::const_iterator it; it = std::find_if(items.begin(), items.end(), [key](const Item& item) -> bool { return item.getID() == key; }); // Ignore any Item id not found in items if (it != items.end()) { ItemStack stack(*it, quantity); const char* result = (inv->addItems(stack) ? "Stored" : "Discarded"); cout << " " << result << " " << stack << "\n"; } } } storage.emplace_back(inv); return storage; } //------------------------------------------------------------------------------ void printItems(const vector<Item>& items) { cout << "\n" << "Item List:" << "\n"; for (const Item& i : items) { cout << " " << i << "\n"; } cout << "\n"; } //------------------------------------------------------------------------------ void printInventories(const vector<unique_ptr<Inventory>>& storage) { cout << "Storage Summary:" << "\n"; for (const unique_ptr<Inventory>& chest : storage) { cout << *chest << "\n"; } }
Pointers/TestInventory.cpp
#include <iostream> #include <fstream> #include <string> #include <cstdlib> #include <vector> #include <sstream> #include <algorithm> #include <functional> #include "Item.h" #include "ItemStack.h" #include "Inventory.h" #include "utilities.h" #include "bodgeUnitTest.h" //----------------------------------------------------------------------------- // Inventory Unit Tests - Support Data //----------------------------------------------------------------------------- const Inventory EMPTY_INVENTORY; const std::vector<Item> TEST_ITEMS = { {0, "Air"}, {1, "Dirt"}, {2, "Potato"}, {3, "Makefile"}, {4, "Procrastination"} }; //----------------------------------------------------------------------------- // Inventory - Unit Tests - Test Functions //----------------------------------------------------------------------------- bool testDefaultConstructor() { bodgeAssert(EMPTY_INVENTORY.utilizedSlots() == 0); bodgeAssert(EMPTY_INVENTORY.emptySlots() == 10); bodgeAssert(EMPTY_INVENTORY.totalSlots() == 10); bodgeAssert(!EMPTY_INVENTORY.isFull()); // I should really check display() and/or operator<< here. However, I will // do that in a seperate `testDisplay` function return true; } //----------------------------------------------------------------------------- bool testConstructorSizeN() { Inventory invWith8Slots(8); bodgeAssert(invWith8Slots.utilizedSlots() == 0); bodgeAssert(invWith8Slots.emptySlots() == 8); bodgeAssert(invWith8Slots.totalSlots() == 8); bodgeAssert(!invWith8Slots.isFull()); // I should really check display() and/or operator<< here. However, I will // do that in a seperate `testDisplay` function return true; } //----------------------------------------------------------------------------- /** * Add ItemStacks to an Inventory without filling the Inventory or attempting * to add duplicate Items */ bool testAddItemStackNoCheck() { std::vector<ItemStack> stacksToAdd = { {TEST_ITEMS[0], 1}, {TEST_ITEMS[1], 4}, {TEST_ITEMS[2], 8} }; Inventory aBag(4); aBag.addItems(stacksToAdd[0]); aBag.addItems(stacksToAdd[1]); aBag.addItems(stacksToAdd[2]); bodgeAssert(!aBag.isFull()); bodgeAssert(aBag.utilizedSlots() == 3); bodgeAssert(aBag.emptySlots() == 1); bodgeAssert(aBag.totalSlots() == 4); // Retrieve each of the items and check that they were added Inventory::const_iterator it = aBag.begin(); bodgeAssert(*(it++) == stacksToAdd[0]); bodgeAssert(*(it++) == stacksToAdd[1]); bodgeAssert(*(it++) == stacksToAdd[2]); // Check that there are no more ItemStacks to retrieve bodgeAssert(it == aBag.end()); return true; } //----------------------------------------------------------------------------- /** * Add ItemStacks to an Inventory without filling the Inventory or attempting * to add duplicate Items */ bool testAddItemWithDuplicateItems() { std::vector<ItemStack> stacksToAdd = { {TEST_ITEMS[0], 1}, {TEST_ITEMS[1], 4}, {TEST_ITEMS[1], 5} }; Inventory aBag(4); aBag.addItems(stacksToAdd[0]); aBag.addItems(stacksToAdd[1]); aBag.addItems(stacksToAdd[2]); bodgeAssert(!aBag.isFull()); bodgeAssert(aBag.utilizedSlots() == 2); bodgeAssert(aBag.emptySlots() == 2); bodgeAssert(aBag.totalSlots() == 4); // Retrieve each of the items and check that they were added Inventory::const_iterator it = aBag.begin(); bodgeAssert(*(it++) == stacksToAdd[0]); // Expect the merged stack to be returned. ItemStack mergedStack(TEST_ITEMS[1], 9); const ItemStack& retrieved = *it; bodgeAssert(retrieved == mergedStack); bodgeAssert(retrieved.size() == 9); it++; // Check that there are no more ItemStacks to retrieve bodgeAssert(it == aBag.end()); return true; } //----------------------------------------------------------------------------- /** * Add ItemStacks to an Inventory and fill it. * Then try to add one more ItemStack. */ bool testAddItemAfterFull() { std::vector<ItemStack> stacksToAdd = { {TEST_ITEMS[0], 1}, {TEST_ITEMS[1], 4}, {TEST_ITEMS[4], 8} }; Inventory aBag(2); aBag.addItems(stacksToAdd[0]); aBag.addItems(stacksToAdd[1]); bodgeAssert(aBag.isFull()); bodgeAssert(aBag.utilizedSlots() == 2); bodgeAssert(aBag.emptySlots() == 0); bodgeAssert(aBag.totalSlots() == 2); // This add should fail // Procrastination is bad bodgeAssert(!(aBag.addItems(stacksToAdd[2]))); // Retrieve each of the items and check that they were added Inventory::const_iterator it = aBag.begin(); bodgeAssert(*(it++) == stacksToAdd[0]); bodgeAssert(*(it++) == stacksToAdd[1]); // Check that there are no more ItemStacks to retrieve bodgeAssert(it == aBag.end()); return true; } //----------------------------------------------------------------------------- bool testDisplay() { const std::vector<ItemStack> stacksToAdd = { {TEST_ITEMS[0], 1}, {TEST_ITEMS[1], 4} }; // Set up the expected strings for each ItemStack std::vector<std::string> stacksAsStrings(stacksToAdd.size()); for (unsigned int i = 0; i < stacksToAdd.size(); ++i) { stacksAsStrings[i] = toStr(stacksToAdd[i]); } // Set up the test Inventory Inventory aBag(2); aBag.addItems(stacksToAdd[0]); aBag.addItems(stacksToAdd[1]); // Check for the expected ItemStack lines within the larger output const std::string bagString = toStr(aBag); bodgeAssert(bagString.find(stacksAsStrings[0]) != std::string::npos); bodgeAssert(bagString.find(stacksAsStrings[1]) != std::string::npos); // Check for the Summary line const std::string expected = "-Used " + std::to_string(aBag.utilizedSlots()) + " of " + std::to_string(aBag.totalSlots()) + " slots"; bodgeAssert(toStr(aBag).find(expected) != std::string::npos); //-------------------------------------------------------------------------- // Check the entire output string. This should really be a seperate test //-------------------------------------------------------------------------- const std::string expectedOverall = " " + expected + "\n" + " " + stacksAsStrings[0] + "\n" + " " + stacksAsStrings[1] + "\n"; bodgeAssert(bagString == expectedOverall); return true; } //----------------------------------------------------------------------------- bool testCopyConstructorForEmpty() { Inventory aCopy(EMPTY_INVENTORY); bodgeAssert(aCopy.utilizedSlots() == 0); bodgeAssert(aCopy.emptySlots() == 10); bodgeAssert(aCopy.totalSlots() == 10); bodgeAssert(!aCopy.isFull()); // Check that both have the same data and are distinct copies bodgeAssert(aCopy == EMPTY_INVENTORY); bodgeAssert(&aCopy != &EMPTY_INVENTORY); // bodgeAssert(aCopy.begin() != EMPTY_INVENTORY.begin()); return true; } //----------------------------------------------------------------------------- bool testCopyConstructor() { const std::vector<ItemStack> stacksToAdd = { {TEST_ITEMS[0], 1}, {TEST_ITEMS[1], 4}, {TEST_ITEMS[4], 8} }; Inventory source(4); for (const ItemStack& next : stacksToAdd) { source.addItems(next); } Inventory aCopy(source); bodgeAssert(aCopy.utilizedSlots() == 3); bodgeAssert(aCopy.emptySlots() == 1); bodgeAssert(aCopy.totalSlots() == 4); bodgeAssert(!aCopy.isFull()); // Check that both have the same data and are distinct copies bodgeAssert(aCopy == source); bodgeAssert(&aCopy != &source); using ConstIterator = Inventory::const_iterator; ConstIterator it = source.begin(); for (const ItemStack& nextToCheck : aCopy) { bodgeAssert(nextToCheck == *it); // Compare ItemStacks bodgeAssert(&nextToCheck != &(*(it++))); // Compare Memory Addresses } return true; } //----------------------------------------------------------------------------- bool testAssignmentOperator() { const std::vector<ItemStack> stacksToAdd = { {TEST_ITEMS[0], 1}, {TEST_ITEMS[1], 4}, {TEST_ITEMS[4], 8} }; Inventory source(4); for (const ItemStack& next : stacksToAdd) { source.addItems(next); } Inventory aCopy = source; bodgeAssert(aCopy.utilizedSlots() == 3); bodgeAssert(aCopy.emptySlots() == 1); bodgeAssert(aCopy.totalSlots() == 4); bodgeAssert(!aCopy.isFull()); // Check that both have the same data and are distinct copies bodgeAssert(aCopy == source); bodgeAssert(&aCopy != &source); using ConstIterator = Inventory::const_iterator; ConstIterator it = source.begin(); for (const ItemStack& nextToCheck : aCopy) { bodgeAssert(nextToCheck == *it); // Compare ItemStacks bodgeAssert(&nextToCheck != &(*(it++))); // Compare Memory Addresses } return true; } //----------------------------------------------------------------------------- int main(int argc, char** argv) { UnitTestPair inventoryTests[] = { {testDefaultConstructor, "testDefaultConstructor"}, {testConstructorSizeN, "testConstructorSizeN"}, {testAddItemStackNoCheck, "testAddItemStackNoCheck"}, {testAddItemWithDuplicateItems, "testAddItemWithDuplicateItems"}, {testAddItemAfterFull, "testAddItemAfterFull"}, {testCopyConstructorForEmpty, "testCopyConstructorForEmpty"}, {testCopyConstructor, "testCopyConstructor"}, {testAssignmentOperator, "testAssignmentOperator"}, {testDisplay, "testDisplay"}, }; std::cout << "Inventory:" << "\n"; for (const UnitTestPair& testPair : inventoryTests) { runTest(testPair.first, testPair.second); } return 0; }
Pointers/TestItemStack.cpp
#include <iostream> #include <fstream> #include <string> #include <cstdlib> #include <vector> #include <sstream> #include <algorithm> #include <functional> #include "Item.h" #include "ItemStack.h" #include "Inventory.h" #include "utilities.h" #include "bodgeUnitTest.h" //----------------------------------------------------------------------------- // ItemStack Unit Tests - Support Data //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // ItemStack - Unit Tests - Test Functions //----------------------------------------------------------------------------- bool testDefaultConstructor() { ItemStack generic; const Item& theItem = generic.getItem(); bodgeAssert(theItem.getID() == 0); bodgeAssert(theItem.getName() == "Air"); bodgeAssert(generic.size() == 0); return true; } bool testSecondConstructor() { Item aCoolItem(32, "Ice"); ItemStack aStack(aCoolItem, 9001); const Item& theRetrievedItem = aStack.getItem(); bodgeAssert(theRetrievedItem.getID() == 32); bodgeAssert(theRetrievedItem.getName() == "Ice"); bodgeAssert(aStack.size() == 9001); return true; } bool testCopyConstructor() { Item aCoolItem(32, "More Ice"); ItemStack originalStack(aCoolItem, 9002); Item theRetrievedItem = originalStack.getItem(); ItemStack aCopy(originalStack); bodgeAssert(theRetrievedItem.getID() == 32); bodgeAssert(theRetrievedItem.getName() == "More Ice"); bodgeAssert(originalStack.size() == 9002); theRetrievedItem = aCopy.getItem(); bodgeAssert(theRetrievedItem.getID() == 32); bodgeAssert(theRetrievedItem.getName() == "More Ice"); bodgeAssert(aCopy.size() == 9002); return true; } bool testAssignment() { Item aCoolItem(32, "Even More Ice"); ItemStack originalStack(aCoolItem, 9002); Item theRetrievedItem = originalStack.getItem(); ItemStack aCopy = originalStack; bodgeAssert(theRetrievedItem.getID() == 32); bodgeAssert(theRetrievedItem.getName() == "Even More Ice"); bodgeAssert(originalStack.size() == 9002); theRetrievedItem = aCopy.getItem(); bodgeAssert(theRetrievedItem.getID() == 32); bodgeAssert(theRetrievedItem.getName() == "Even More Ice"); bodgeAssert(aCopy.size() == 9002); return true; } bool testAddItems() { Item aWarmItem(120, "Hot Tea"); ItemStack aStack(aWarmItem, 12); aStack.addItems(42); const Item& theRetrievedItem = aStack.getItem(); bodgeAssert(theRetrievedItem.getID() == 120); bodgeAssert(theRetrievedItem.getName() == "Hot Tea"); bodgeAssert(aStack.size() == 54); return true; } bool testAddItemsFrom() { Item aHotItem(200, "Mix Tape"); ItemStack aStack(aHotItem, 12); ItemStack aSecondStack(aHotItem, 24); aStack.addItemsFrom(aSecondStack); const Item& theRetrievedItem = aStack.getItem(); bodgeAssert(theRetrievedItem.getID() == 200); bodgeAssert(theRetrievedItem.getName() == "Mix Tape"); bodgeAssert(aStack.size() == 36); // aSecondStack does not need to be checked due to const correctness return true; } bool testLogicalEquivalence() { Item aJoke(42, "Emacs & PHP code"); Item anIdenticalJoke(42, "Emacs & PHP code"); Item anImposterJoke(42, "Code::Blocks"); ItemStack stack1(aJoke, 9000); ItemStack stack2(anIdenticalJoke, 337); ItemStack stack3(anImposterJoke, 12); // Only the Item IDs should be considered. bodgeAssert(stack1 == stack2); bodgeAssert(stack1 == stack3); bodgeAssert(stack2 == stack3); return true; } bool testLessThan() { Item aJoke(42, "Emacs & PHP code"); Item anIdenticalJoke(42, "Emacs & PHP code"); Item funToys(1, "Vim & Python or Rust code"); ItemStack stack1(aJoke, 9000); ItemStack stack2(anIdenticalJoke, 337); ItemStack stack3(funToys, 12); // Only the Item IDs should be considered. bodgeAssert(!(stack1 < stack2)); bodgeAssert(!(stack2 < stack1)); // Vim should always be #1, especially if Rust & Python are involved bodgeAssert(stack3 < stack1); bodgeAssert(stack3 < stack2); return true; } bool testDisplay() { Item aWarmItem(120, "Hot Tea"); ItemStack stack1(aWarmItem, 7); const std::string expected1 = "( 7) " + aWarmItem.getName(); bodgeAssert(expected1 == toStr(stack1)); //-------------------------------------------------------------------------- Item healthyItem(99, "Celery"); ItemStack stack2(healthyItem, 42); const std::string expected2 = "(42) " + healthyItem.getName(); bodgeAssert(expected2 == toStr(stack2)); return true; } bool testSwap() { Item aWarmItem(120, "Hot Tea"); ItemStack stack1(aWarmItem, 12); Item aCoolItem(32, "Ice"); ItemStack stack2(aCoolItem, 9001); swap(stack1, stack2); bodgeAssert(stack1.getItem().getName() == "Ice"); bodgeAssert(stack1.getItem().getID() == 32); bodgeAssert(stack1.size() == 9001); bodgeAssert(stack2.getItem().getName() == "Hot Tea"); bodgeAssert(stack2.getItem().getID() == 120); bodgeAssert(stack2.size() == 12); return true; } //----------------------------------------------------------------------------- int main(int argc, char** argv) { UnitTestPair itemStackTests[] = { {testDefaultConstructor, "testDefaultConstructor"}, {testSecondConstructor, "testSecondConstructor"}, {testCopyConstructor, "testCopyConstructor"}, {testAssignment, "testAssignment"}, {testAddItems, "testAddItems"}, {testAddItemsFrom, "testAddItemsFrom"}, {testLogicalEquivalence, "testLogicalEquivalence"}, {testLessThan, "testLessThan"}, {testDisplay, "testDisplay"}, {testSwap, "testSwap"} }; std::cout << "ItemStack:" << "\n"; for (const UnitTestPair& testPair : itemStackTests) { runTest(testPair.first, testPair.second); } return 0; }
Pointers/utilities.cpp
#include <string> #include <cstdlib> #include "utilities.h" /** * Trim leading and trailing whitespace from a string. * * @param str string to prune * * @pre str is nonempty */ void trim(std::string& str) { if (str.empty()) { return; } const int first_nonspace = str.find_first_not_of(" \t"); const int last_non_space = str.find_last_not_of(" \t"); str = str.substr(first_nonspace, last_non_space + 1); }
Pointers/utilities.h
#ifndef UTILITIES_H_INCLUDED #define UTILITIES_H_INCLUDED #include <string> #include <sstream> /** * Trim leading and trailing whitespace from a string. * * @param str string to prune * * @pre str is nonempty */ void trim(std::string& str); /** * Helper function for types where std::to_string is not available. * * Convert any type with an operator<< defined to a std::string */ template<class T> std::string toStr(const T& thing) { std::ostringstream outs; outs << thing; return outs.str(); } #endif