Project 12.08 Section 12 ⚡ Embedded Relevance: High Adapter Pattern Composition ListStack Layering

12.08 Composition vs Inheritance: Wrapping List Primitives in Stack Interfaces

Executive Summary: Demonstrating the Adapter design pattern by implementing a Stack interface over an underlying LinkedList.

💻 1. Annotated Source Code

#ifndef LIST_H
#define LIST_H

class List {
public:
	virtual void add(int newEntry) = 0;
	virtual void add(int newEntry, int position) = 0;
	virtual void set(int newEntry, int position) = 0;

	virtual bool contains(int entry) const = 0;
	virtual int find(int entry) const = 0;
	virtual int remove(int position) = 0;
	virtual void makeEmpty() = 0;

	virtual int size() const = 0;
	virtual bool isEmpty() const = 0;
	virtual void printList() const = 0;

};

#endif
#ifndef LINKED_LIST_H
#define LINKED_LIST_H

#include <iostream>
#include "List.h"
using namespace std;

// ---------------- Node -------------------- //
class Node {
	public:
		Node(int data, Node* next) {
			this->data = data;
			this->next = next;
		}//end ctor

		int getData() const {
			return data;
		}

		void setData(int data) {
			this->data = data;
		}

		Node* getNext() const {
			return next;
		}

		void setNext(Node* next) {
			this->next = next;
		}

	private:
		int data;
		Node* next;
};

// --------------- Linked List ---------------------//
class LinkedList : public List {
	public:
		LinkedList() {
			mHead = nullptr;
			mNumElements = 0;
		}//end ctor

		virtual ~LinkedList() {
			makeEmpty();  //clean up the nodes
		}//end dtor

		void add(int newEntry) override {
			Node* newNode = new Node(newEntry, nullptr);

			if (mHead == nullptr) {
				mHead = newNode;
			}
			else {
				Node* walker = mHead;                        
				while (walker->getNext() != nullptr) {
					walker = walker->getNext();
				}//end while

				walker->setNext(newNode);
			}
			mNumElements++;
		}//end add


		void add(int newEntry, int position) override {
			if (position < 0 || position > mNumElements) {
				cout << "Error: Cannot add at specified position." << endl;
				return;
			}

			Node* newNode = new Node(newEntry, nullptr);

			if (position == 0) {
				newNode->setNext(mHead);
				mHead = newNode;
			}
			else {
				Node* nodeBefore = mHead;

				for (int i = 0; i < position - 1; i++) {
					nodeBefore = nodeBefore->getNext();
				}//end for 
				                                                         
				newNode->setNext(nodeBefore->getNext());     
				nodeBefore->setNext(newNode);
			}

			mNumElements++;
		}//end add

		void set(int newEntry, int position) override {
			if (position < 0 || position > mNumElements) {
				cout << "Error:  Invalid position." << endl;
				return;
			}

			Node* walker = mHead;

			for (int i = 0; i < position; i++) {
				walker = walker->getNext();
			}//end for 

			walker->setData(newEntry);
		}//end set

		bool contains(int entry) const override {
			return find(entry) != -1;
		}//end contains

		int find(int entry) const override {
			Node* walker = mHead;
			int index = 0;

			while (walker != nullptr) {
				if (walker->getData() == entry) {
					return index;
				}
				walker = walker->getNext();
				index++;
			}//end while

			return -1;

		}//end find

		int remove(int position) override {
			if (position < 0 || position >= mNumElements) {
				cout << "Error:  Cannot remove at specified position." << endl;
				return 0;
			}

			int dataToReturn = 0;

			if (position == 0) {
				Node* temp = mHead;
				dataToReturn = temp->getData();
				mHead = mHead->getNext();
				delete temp;
			}
			else {
				Node* nodeBefore = mHead;
				for (int i = 0; i < position - 1; i++) {
					nodeBefore = nodeBefore->getNext();
				}//end for

				Node* nodeToRemove = nodeBefore->getNext();
				Node* nodeAfter = nodeToRemove->getNext();
				dataToReturn = nodeToRemove->getData();

				nodeBefore->setNext(nodeAfter);
				delete nodeToRemove;
			}

			mNumElements--;
			return dataToReturn;
		}//end remove 

		void makeEmpty() override {
			Node* temp;
			while (mHead != nullptr) {
				temp = mHead;
				mHead = mHead->getNext();
				delete temp;
			}
		}//end makeEmpty

		int size() const override {
			return mNumElements;
		}//end size

		bool isEmpty() const override {
			return mNumElements == 0;
		}//end isEmpty

		void printList() const override {
			Node* walker = mHead;
			while (walker != nullptr) {
				cout << walker->getData() << endl;
				walker = walker->getNext();
			}//end while
		}

	private:
		Node* mHead;
		int mNumElements;
};



#endif 
#ifndef STACK_H
#define STACK_H

class Stack {
public:
	virtual void push(int newEntry) = 0;
	virtual int pop() = 0;
	virtual int peek()  = 0;
	virtual bool isEmpty() const = 0;
	virtual void makeEmpty() = 0;
};

#endif 
#ifndef LIST_STACK_H
#define LIST_STACK_H

#include <iostream>
#include "Stack.h"
#include "LinkedList.h"
using namespace std;

class ListStack : public Stack {
	public:
		ListStack() {} 
		//end ctor

		virtual ~ListStack() {
			makeEmpty();
		}//end dtor

		void push(int newEntry) override {
			linkedListStack.add(newEntry, 0);
		}//end push

		int pop() override {
			if (isEmpty()) {
				cout << "Error: Cannot pop from an empty stack!" << endl;
				return 0;
			}

			return linkedListStack.remove(0);
		}//end pop

		int peek() override {
			if (isEmpty()) {
				cout << "Error: Cannot peek an empty stack!" << endl;
				return 0;
			}
			
			int data = linkedListStack.remove(0);
			linkedListStack.add(data, 0);

			return data;
		}//end peek

		bool isEmpty() const override {
			return linkedListStack.isEmpty();
		}  //end isEmpty

		void makeEmpty() override {
			linkedListStack.makeEmpty();
		}

	private:
		LinkedList linkedListStack;
};


#endif 
#include <iostream>
#include "ListStack.h"
using namespace std;

void printStack(ListStack& stack);

int main() {
	ListStack stack;

	stack.push(100);
	stack.push(150);
	stack.push(222);
	stack.push(71);
	stack.push(53);
	stack.push(125);

	printStack(stack);
	cout << "Again to verify it's intact:" << endl;
	printStack(stack);

	//cout << "Top of stack is: " << stack.peek() << endl;

	//while (!stack.isEmpty()) {
	//	cout << stack.pop() << endl;
	//}
	return 0;
}

void printStack(ListStack& stack) {
	ListStack temp;
	int data;

	while (!stack.isEmpty()) {
		data = stack.pop();
		cout << data << endl;
		temp.push(data);
	}//end while

	while (!temp.isEmpty()) {
		stack.push(temp.pop());
	}

}//end printStack

📐 2. Architecture & UML Class Model

📐 ListStack Adapter Pattern over LinkedList Backend
+ Public - Private # Protected
<<template class>> LinkedList<T> Adaptee Container
-headPtr : ListNode<T>*
-itemCount : size_t
+insert(pos: int, entry: const T&) : bool
+remove(pos: int) : bool
+getEntry(pos: int) : T const
+isEmpty() : bool const
<<template class>> ListStack<T> Stack Adapter
-listPtr : LinkedList<T>*
+ListStack()
+~ListStack()
+push(newEntry: const T&) : bool[delegates to listPtr->insert(1, entry)]
+pop() : bool[delegates to listPtr->remove(1)]
+peek() : T const[delegates to listPtr->getEntry(1)]
+isEmpty() : bool const
🔗 Architectural Relationships & Hierarchy
ListStack<T> ◆── adapts interface (composition) ◆── LinkedList<T>

📚 3. Core C++ Concepts Deep-Dive

The Adapter Pattern

Adapting an existing interface (List) to satisfy a target interface (Stack) using composition.

⚡ 4. Embedded Systems & Hardware Reality

Abstraction Cost

Inlined adapter wrappers have zero runtime cost under compiler optimization (-O2).

💡 5. Production-Ready Embedded Refactoring

💡 Production-Ready Refactor
template <typename Container>
class StackAdapter {
    Container c_;
public:
    void push(typename Container::value_type v) { c_.push_back(v); }
};

📝 Knowledge Verification Quiz

Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.

Q1. What design pattern does ListStack implement?
A Adapter (Wrapper) Pattern
B Singleton Pattern
C Observer Pattern
D Visitor Pattern
Detailed Explanation: ListStack adapts the general List interface into a restricted LIFO Stack interface.