Rust version in src/main.rs (Cargo project) and a C++ port in cpp/main.cpp using raw pointers, both with VS Code build tasks and gdb-based debug configs. Vendors vscode-debug-visualizer and visualization as submodules for reference while building a custom node-table graph visualizer. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
101 lines
2.8 KiB
C++
101 lines
2.8 KiB
C++
#include <iostream>
|
|
#include <optional>
|
|
#include <sstream>
|
|
|
|
// A single node containing data and a raw pointer to the next node
|
|
template <typename T>
|
|
struct Node {
|
|
T data;
|
|
Node<T>* next;
|
|
};
|
|
|
|
// The main wrapper for the linked list tracking the head node
|
|
template <typename T>
|
|
class LinkedList {
|
|
public:
|
|
LinkedList() : head_(nullptr) {}
|
|
|
|
~LinkedList() {
|
|
Node<T>* current = head_;
|
|
while (current) {
|
|
Node<T>* next = current->next;
|
|
delete current;
|
|
current = next;
|
|
}
|
|
}
|
|
|
|
// Raw-pointer ownership can't be safely duplicated by a shallow copy
|
|
LinkedList(const LinkedList&) = delete;
|
|
LinkedList& operator=(const LinkedList&) = delete;
|
|
|
|
// Add a new element to the front of the list
|
|
void push_front(T data) {
|
|
head_ = new Node<T>{std::move(data), head_};
|
|
}
|
|
|
|
// Remove and return the front element of the list
|
|
std::optional<T> pop_front() {
|
|
if (!head_) {
|
|
return std::nullopt;
|
|
}
|
|
Node<T>* old_head = head_;
|
|
T data = std::move(old_head->data);
|
|
head_ = old_head->next;
|
|
delete old_head;
|
|
return data;
|
|
}
|
|
|
|
// Read the front element without removing it
|
|
const T* peek_front() const {
|
|
return head_ ? &head_->data : nullptr;
|
|
}
|
|
|
|
// Build a JSON string describing the list as a graph, for the VS Code
|
|
// "Debug Visualizer" extension: watch `list.toDebugGraph()` while paused.
|
|
// Schema: https://hediet.github.io/visualization/docs/visualization-data-schema.json
|
|
std::string toDebugGraph() const {
|
|
std::ostringstream json;
|
|
json << "{\"kind\":{\"graph\":true},\"nodes\":[";
|
|
for (Node<T>* n = head_; n; n = n->next) {
|
|
if (n != head_) json << ",";
|
|
json << "{\"id\":\"" << n << "\",\"label\":\"" << n->data << "\"}";
|
|
}
|
|
json << "],\"edges\":[";
|
|
for (Node<T>* n = head_; n && n->next; n = n->next) {
|
|
if (n != head_) json << ",";
|
|
json << "{\"from\":\"" << n << "\",\"to\":\"" << n->next << "\"}";
|
|
}
|
|
json << "]}";
|
|
return json.str();
|
|
}
|
|
|
|
private:
|
|
Node<T>* head_;
|
|
};
|
|
|
|
// Force instantiation of every member, including toDebugGraph(), which
|
|
// nothing in this file calls directly -- without this, gdb has no symbol
|
|
// to invoke from the debugger's watch expression.
|
|
template class LinkedList<int>;
|
|
|
|
int main() {
|
|
LinkedList<int> list;
|
|
|
|
// Demonstrate pushing items
|
|
list.push_front(10);
|
|
list.push_front(20);
|
|
list.push_front(30);
|
|
|
|
// Demonstrate peeking at the top item
|
|
if (const int* top = list.peek_front()) {
|
|
std::cout << "Top element: " << *top << "\n"; // Output: 30
|
|
}
|
|
|
|
// Demonstrate popping items
|
|
while (auto value = list.pop_front()) {
|
|
std::cout << "Popped: " << *value << "\n";
|
|
}
|
|
|
|
return 0;
|
|
}
|