Files
CPP-LinkedList/include/linkedlist.h

520 lines
13 KiB
C
Raw Normal View History

#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include <concepts>
#include <cstdlib>
#include <expected>
#include <format>
#include <new>
#include <print>
/**
* @brief Concept that ensures a type supports equality comparison via operator==.
*
* @tparam T Type to be checked.
*/
template <typename T>
concept HasEqual = requires (T a, T b) {
{ a == b } -> std::convertible_to<bool>;
};
/**
* @brief Concept defining the requirements for types supported by LinkedList.
*
* A valid type must:
* - Be formattable (usable with std::print / std::format)
* - Support equality comparison
*
* @tparam T Type to be checked.
*/
template <typename T>
concept LinkedListSupported =
std::formattable<T, char> &&
std::equality_comparable<T> &&
HasEqual<T>;
/**
* @brief Error codes for LinkedList operations.
*/
enum class LinkedListErr {
LINKEDLIST_OK, /**< Operation completed successfully */
LINKEDLIST_BAD_ALLOC, /**< Memory allocation failed */
LINKEDLIST_OUT_OF_BOUNDS, /**< Index is out of valid range */
LINKEDLIST_INVALID_SIZE, /**< Invalid size provided */
LINKEDLIST_NOT_FOUND, /**< Element not found */
LINKEDLIST_EMPTY, /**< The list is empty */
};
/**
* @brief Converts a LinkedListErr value to its string representation.
*
* Provides a human-readable, null-terminated string corresponding to
* the given LinkedListErr enumeration value. This function is primarily
* intended for logging, debugging, and formatting purposes (e.g., with
* std::format / std::print).
*
* @param err The LinkedListErr value to convert.
*
* @return const char* A pointer to a statically allocated string literal
* representing the error. The returned pointer remains valid for the
* lifetime of the program and must not be freed.
*
* @note This function does not throw exceptions.
* @note If an unknown or invalid enumeration value is provided, the
* string "UNKNOWN_ERROR" is returned.
*
* @see std::formatter<LinkedListErr>
*/
[[nodiscard]] inline const char* to_string(LinkedListErr err) {
switch (err) {
case LinkedListErr::LINKEDLIST_OK:
return "OK";
case LinkedListErr::LINKEDLIST_BAD_ALLOC:
return "The program couldn't allocate memory for the list.";
case LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS:
return "The index is out of bounds.";
case LinkedListErr::LINKEDLIST_INVALID_SIZE:
return "Is an invalid size fo the list.";
case LinkedListErr::LINKEDLIST_NOT_FOUND:
return "The value was not found.";
case LinkedListErr::LINKEDLIST_EMPTY:
return "The list is empty";
default:
return "UNKNOWN_ERROR";
}
}
2026-03-22 00:52:57 -06:00
/**
* @brief Formatter specialization for LinkedListErr.
*
* Enables formatting of LinkedListErr values with std::format and std::print
* by converting the enum to a human-readable string using to_string().
*
* This implementation delegates formatting to the formatter for const char*,
* allowing full compatibility with standard formatting options (e.g., alignment).
*
* @note This specialization makes LinkedListErr satisfy std::formattable.
*
* @see to_string(LinkedListErr)
*/
template <>
struct std::formatter<LinkedListErr> : std::formatter<const char*> {
auto format(LinkedListErr err, format_context& ctx) const {
return std::formatter<const char*>::format(to_string(err), ctx);
}
};
/**
* @brief Node of a singly linked list.
*
* @tparam T Type of data stored in the node.
*/
template<LinkedListSupported T>
class Node {
public:
T data; /**< Data stored in the node */
Node<T>* next; /**< Pointer to the next node */
};
/**
* @brief Implementation of a singly linked list.
*
* A dynamic data structure that stores elements of type T
* using nodes connected via pointers.
*
* @tparam T Type of elements stored in the list.
*/
template<LinkedListSupported T>
class LinkedList {
private:
size_t size; /**< Number of elements in the list */
Node<T>* head; /**< Pointer to the first node */
Node<T>* tail; /**< Pointer to the last node */
public:
/**
* @brief Constructs an empty linked list.
*/
LinkedList();
/**
* @brief Destroys the linked list.
*
* Releases all dynamically allocated nodes.
*/
~LinkedList();
/**
* @brief Retrieves the element at a given index.
*
* @param index Position of the element.
* @return std::expected<T, LinkedListErr> The element or an error code.
*/
std::expected<T, LinkedListErr> get(size_t index);
/**
* @brief Appends an element to the end of the list.
*
* @param value Value to insert.
* @return LinkedListErr Status code of the operation.
*/
LinkedListErr append(const T& value);
/**
* @brief Inserts an element at the beginning of the list.
*
* @param value Value to insert.
* @return LinkedListErr Status code of the operation.
*/
LinkedListErr prepend(const T& value);
/**
* @brief Removes and returns the last element of the list.
*
* @return std::expected<T, LinkedListErr> Removed element or an error.
*/
std::expected<T, LinkedListErr> pop();
/**
* @brief Removes the element at a specific index.
*
* @param index Index of the element to remove.
* @return LinkedListErr Status code.
*/
LinkedListErr remove(size_t index);
/**
* @brief Updates the value of an element at a given index.
*
* @param index Index of the element.
* @param value New value.
* @return LinkedListErr Status code.
*/
LinkedListErr set(size_t index, const T& value);
/**
* @brief Inserts a value at a specified index.
*
* @param index Target position.
* @param value Value to insert.
* @return LinkedListErr Status code.
*/
LinkedListErr insert(size_t index, const T& value);
/**
* @brief Searches for a value in the list.
*
* @param value Value to search for.
* @return std::expected<size_t, LinkedListErr> Index of the element or error.
*/
std::expected<size_t, LinkedListErr> find(const T& value);
/**
* @brief Prints all elements in the list.
*
* @return LinkedListErr Status code.
*/
LinkedListErr print();
/**
* @brief Checks whether the list is empty.
*
* @return true if the list contains no elements, false otherwise.
*/
bool is_empty();
/**
* @brief Returns the number of elements in the list.
*
* @return size_t Number of stored elements.
*/
size_t len();
/**
* @brief Clears the list by deleting all its elements.
*
* Frees all nodes and resets the list to an empty state.
*
* @note After this call, the list size is 0 and both head and tail are nullptr.
*/
void clear();
};
template <LinkedListSupported T>
LinkedList<T>::LinkedList() {
size = 0;
head = nullptr;
tail = nullptr;
}
template <LinkedListSupported T>
LinkedList<T>::~LinkedList() {
Node<T> *current = this->head;
while (current != nullptr) {
Node<T> *temp = current;
current = current->next;
delete temp;
}
}
template <LinkedListSupported T>
std::expected<T, LinkedListErr> LinkedList<T>::get(size_t index) {
if (index >= this->size) {
return std::unexpected(LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS);
}
if (index == this->size - 1) {
return tail->data;
}
Node<T> *current = this->head;
for (size_t i = 0; i < index ; i++) {
current = current->next;
}
return current->data;
}
template <LinkedListSupported T>
LinkedListErr LinkedList<T>::set(size_t index, const T& val) {
if (index >= this->size) {
return LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS;
}
if (index == this->size - 1) {
this->tail->data = val;
}
Node<T> *current = this->head;
for (size_t i = 0; i < index; i++) {
current = current->next;
}
current->data = val;
return LinkedListErr::LINKEDLIST_OK;
}
template <LinkedListSupported T>
LinkedListErr LinkedList<T>::insert(size_t index, const T& value) {
2026-03-21 23:05:37 -06:00
if (this->is_empty() && index == 0) {
return this->append(value);
}
if (index > this->size) {
return LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS;
}
if (index == 0) {
return this->prepend(value);
}
if (index == this->size) {
return this->append(value);
}
Node<T>* current = this->head;
for (size_t i = 1; i < index; i++) {
current = current->next;
}
Node<T>* new_node = nullptr;
try {
new_node = new Node<T>;
} catch (const std::bad_alloc&) {
return LinkedListErr::LINKEDLIST_BAD_ALLOC;
}
new_node->data = value;
new_node->next = current->next;
current->next = new_node;
this->size++;
return LinkedListErr::LINKEDLIST_OK;
}
template <LinkedListSupported T>
LinkedListErr LinkedList<T>::append(const T& value) {
Node<T> *new_node = nullptr;
try {
new_node = new Node<T>;
} catch(std::bad_alloc&) {
return LinkedListErr::LINKEDLIST_BAD_ALLOC;
}
new_node->data = value;
new_node->next = nullptr;
if (this->head == nullptr && this->tail == nullptr) {
this->head = new_node;
this->tail = this->head;
} else {
this->tail->next = new_node;
this->tail = this->tail->next;
}
this->size += 1;
return LinkedListErr::LINKEDLIST_OK;
}
template <LinkedListSupported T>
LinkedListErr LinkedList<T>::prepend(const T& value) {
Node<T> *new_node = nullptr;
try {
new_node = new Node<T>;
} catch(std::bad_alloc&) {
return LinkedListErr::LINKEDLIST_BAD_ALLOC;
}
new_node->data = value;
new_node->next = nullptr;
if (this->head == nullptr && this->tail == nullptr) {
this->head = new_node;
this->tail = this->head;
} else {
new_node->next = this->head;
this->head = new_node;
}
this->size += 1;
return LinkedListErr::LINKEDLIST_OK;
}
template <LinkedListSupported T>
std::expected<T, LinkedListErr> LinkedList<T>::pop() {
if (this->size == 0) {
return std::unexpected(LinkedListErr::LINKEDLIST_EMPTY);
}
if (this->head->next == nullptr) {
T return_val = this->head->data;
delete this->head;
this->head = nullptr;
this->size--;
return return_val;
}
Node<T>* current = this->head;
while (current->next->next != nullptr) {
current = current->next;
}
T return_val = current->next->data;
delete current->next;
current->next = nullptr;
this->size--;
return return_val;
}
template <LinkedListSupported T>
LinkedListErr LinkedList<T>::remove(size_t index) {
if (index >= this->size) {
return LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS;
}
if (index == 0) {
Node<T>* tmp = this->head;
this->head = this->head->next;
delete tmp;
this->size--;
return LinkedListErr::LINKEDLIST_OK;
}
Node<T>* current = this->head;
for (size_t i = 1; i < index; ++i) {
current = current->next;
}
Node<T>* tmp = current->next;
current->next = tmp->next;
if(tmp == this->tail) {
this->tail = current;
}
delete tmp;
this->size--;
return LinkedListErr::LINKEDLIST_OK;
}
template <LinkedListSupported T>
std::expected<size_t, LinkedListErr> LinkedList<T>::find(const T& value) {
Node<T> *current = this->head;
size_t count = 0;
while (current != nullptr) {
if (current->data == value) {
return count;
}
count++;
current = current->next;
}
return std::unexpected(LinkedListErr::LINKEDLIST_NOT_FOUND);
}
template <LinkedListSupported T>
LinkedListErr LinkedList<T>::print() {
if (this->size == 0) {
return LinkedListErr::LINKEDLIST_OK;
}
std::println("[LL]");
std::println(" |\n v");
Node<T> *current = this->head;
while (current != nullptr) {
std::println("Node({})", current->data);
std::println(" |");
std::println(" v");
current = current->next;
}
std::println("NULL");
return LinkedListErr::LINKEDLIST_OK;
}
template <LinkedListSupported T>
size_t LinkedList<T>::len() {
return this->size;
}
2026-03-21 23:05:37 -06:00
template <LinkedListSupported T>
bool LinkedList<T>::is_empty() {
return this->len() < 1;
}
template <LinkedListSupported T>
void LinkedList<T>::clear() {
Node<T>* current = head;
while (current != nullptr) {
Node<T>* next = current->next;
delete current;
current = next;
}
head = nullptr;
tail = nullptr;
size = 0;
}
#endif // !